Omahub
← All plugins
R

Omatop

by Ryan Yogan

System monitor that shows you the culprit. Quiet bar glyph, live dot-matrix dropdown, full-screen diagnostic overlay with vim keys.

Security review

Review recommended · 11 findings

Deterministic scan — not a security guarantee

Low
Risk level
Low
Analyzed commit
d8c4bae
Scanned
2 weeks ago
  • low obfuscation …/src/apps.rs:1189

    Augments a command with octal/hex escape sequences.

    \x2dexec-558d9a36.scope";
  • low obfuscation …/src/proc.rs:75

    Augments a command with octal/hex escape sequences.

    \x2dexec`. Nothing
  • low obfuscation …/src/proc.rs:518

    Augments a command with octal/hex escape sequences.

    \x2dexec"), "xdg-terminal-exec");
  • Registers scheduled or boot-time system tasks.

    systemd-run --unit=omarchy-browser-<ns>`.
  • Augments a command with octal/hex escape sequences.

    \x2dexec-558d9a36.scope` — the same scope as the
  • Augments a command with octal/hex escape sequences.

    \x2dwatch-...  running omarchy-hyprland-monitor-watch
  • Augments a command with octal/hex escape sequences.

    \x2dexec-558d9a36.scope       running xdg-terminal-exec
  • Augments a command with octal/hex escape sequences.

    \x2dexec-558d9a36.scope
  • Augments a command with octal/hex escape sequences.

    \x2dexec-558d9a36.scope' -p Description
  • Augments a command with octal/hex escape sequences.

    \x2dorg.a11y.atspi.Registry.slice/dbus-:1.22-org.a11y.atspi.Registry@0.service` | 1 | a nested **slice** under `app.slice` — matches "`*.service` under `app.slice`" and would land in `apps` as a user-
  • Augments a command with octal/hex escape sequences.

    \x2dexec-558d9a36.scope`), so it is already

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
d8c4bae
Reviewed
2 weeks ago

No malicious or hidden behavior was found in the sampled code. The plugin is a system monitor that reads /proc and /sys, and its process-control actions (stop, pause, restart) are user-initiated and documented. The deterministic scan's high-severity persistence hit and obfuscation flags are false positives: they refer to documentation examples and literal systemd unit-name hyphen escapes, not executable persistence or encoded commands.

  • The high-severity 'persistence' finding is a systemd-run example inside docs/design-review.md, not executable code; no scheduled-task or boot-persistence mechanism exists in the plugin sources.
  • The 'obfuscation' findings are literal \x2d (hyphen) escapes used to match systemd scope names such as xdg\x2dterminal\x2dexec, not hidden or obfuscated commands.
  • The plugin can terminate, freeze, or restart applications and builds a Rust helper on first use; these capabilities are user-consented and presented in the UI, but they are broader than a purely passive monitor.
  • The runtime cargo build downloads build dependencies from crates.io; this is standard and transparent, but it does execute third-party build code on the user's machine.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/ryanyogan/omarchy-omatop --enable
System #bar #quickshell #system

Omatop

A system monitor for Omarchy that shows you the culprit. Quiet glyph in the bar, clean stats on click, and a full-screen instrument cluster on right-click or Super+Ctrl+M. Vim keys everywhere.

Omatop 1.2.2: sampler update notice in the live quick view

Sampler update notice · Quick-view detail · Full monitor

Thirty seconds of the cluster working for a living: the ignition sweep, focusing an App so the dials re-point and its timelines slide into the ledger, the port search, the core row lighting up under real load, and the whole surface recolouring live through Tokyo Night, Catppuccin Latte and Gruvbox. Watch the demo.

What you get

In the bar. A small chip mark. It wears your theme foreground while the machine is calm, then shades amber, orange and red as the kernel reports real pressure. It never moves; the colour is the whole signal. Left click opens the quick view, right click opens the cluster.

The quick view. A compact dot-matrix monitor: large CPU, memory and GPU readings beside recent activity, CPU/GPU temperatures, and a per-core equalizer. Lit cells fade in and out over 180 ms on one shared 25 Hz clock; reduced motion makes them step. The dropdown samples every half second while open, keeping all readings current without rebuilding the supporting rows. Closing restores refreshSeconds. History columns preserve peaks across the last 120 samples (the time span varies with sampling cadence). Network download/upload, disk read/write, swap used/total and fan speed appear when available, with explicit byte units. Click Full monitor, or press o or Enter, to jump to the overlay. Long panels scroll with the wheel, arrow keys or j/k.

The cluster. No card, just instruments on a dark scrim. Four dials sweep on open like a car cluster, then settle into a reading every five seconds and glide to the next: CPU, memory, GPU, temperature. Under them, a trip computer row for net, disk, power, load and uptime. Below that the ledger: every vital on one shared 120-sample axis, advancing with each sample, so a spike in one lines up with a spike in another. Under the CPU timeline, one block per core on the same calm, amber, red ramp, so a single pinned core shows as one hot block while the total still reads 4%. The quick view shows core activity as an equalizer. Hover or press , . to scrub back in time and read every strip at that instant.

Offenders, without the jumping. The panel you actually read: the top Apps by 30 second average CPU and memory, listed alphabetically with themed icons and meter bars. Membership is re-picked at most every 30 seconds, so the set is stable and the numbers move inside it. No row ever leaps to the top because something sneezed.

Apps, not PIDs. One row per application (Chromium is one row, not forty), alphabetical inside User, System, Desktop and Kernel sections. Tab jumps between sections. Rows never reorder by usage, so what you are looking at stays where it is. Focus a row and the dials re-point at that App: the needles glide from the machine's values to Chromium's, and its own timelines slide into the ledger on the same axis. Press o to unfold its processes.

Recent, on top, never moving. Things you just started from a terminal (npm run dev, cargo build, docker compose up) and recently launched apps sit in a strip at the top, newest first. They never get re-sorted by usage, and each shows its listening ports. Type /3000 to find whatever is on port 3000, press x to stop it. That is the whole workflow. 🎯

Actions. x stops (asks first). ss pauses and resumes, using the cgroup freezer so the whole app freezes atomically. r restarts a service. p pins an App in the full monitor and survives restarts. The Desktop bucket (compositor, shell, audio) is read-only, on purpose.

Pressure. Calm, under load, heavy load, or critical, computed from the kernel's pressure stall information (PSI) and swap-in rate, not from a CPU percentage. Temperature only counts once the CPU is actually in its throttle zone. The glyph, the quick view and the cluster all read the same value.

Your theme. Accent and urgent colours come from the theme. Spacing and type follow the shell. Reduce motion is a setting.

Install

omarchy plugin add https://github.com/ryanyogan/omarchy-omatop --enable

Add the Omatop widget to your bar (System category). The first time you open the quick view or the cluster, a card explains that Omatop needs its system monitor, a small Rust helper that reads the machine, and offers Build now and Learn more (what it is, what it reads, why it exists, what it costs). Click Build now or press b; it takes about a minute. Omatop detects when a later plugin release needs a newer sampler and shows an Update notice in the quick view and full monitor. Omarchy ships cargo, so there is nothing else to install. Nothing is built or run at install time.

To update, run omarchy plugin update ryanyogan.omatop and reopen Omatop. If Sampler update available appears, click Update or press b. Omatop rebuilds and restarts the helper, then clears the notice when the new sampler reports its version. Build progress and failures appear in the same view, with Try again if needed. Older samplers without a version field are detected too. If the shell still shows the old UI, run omarchy restart shell.

Manual rebuild, if needed:

cargo build --release --manifest-path "$HOME/.config/omarchy/plugins/ryanyogan.omatop/sampler/Cargo.toml"
omarchy restart shell

Remove it with omarchy plugin remove ryanyogan.omatop. Saved history and pins remain in ~/.local/state/omatop/; remove that directory too if you want to clear them.

Optional hotkey, in ~/.config/hypr/bindings.lua:

o.bind("SUPER + CTRL + M", "System monitor", "omarchy-shell shell toggle ryanyogan.omatop")

Keys

Everything is on the keyboard and the grammar is vim's. Counts work where they make sense (5j, 12G). The mouse works too: hover the ledger to scrub, click a row to focus it, right click to unfold its processes, click the scrim to close.

Move

j k down, up
gg G first row, last row (12G jumps to row 12)
ctrl-d ctrl-u half a page down, up
page down page up a page down, up
H M L top, middle, bottom of the view
tab shift-tab } { next, previous section

Look

enter l focus the App: the dials re-point at it and its timelines join the ledger
h esc unfocus
o space unfold the App's processes
za fold or unfold the section under the cursor
zM zR fold all, unfold all
, . scrub the timelines back, forward (10, steps ten samples)
0 back to live

Find

/ filter by name, command or :port; enter keeps the filter, esc clears it
n N next, previous match
sc sm sg sn sort by cpu, memory, gpu, name

Act

p pin the App in the full monitor; pins survive restarts
ss pause or resume (cgroup freezer, the whole App at once)
x delete stop, after a confirmation
r restart a Service
b build, update or retry the sampler when prompted
? help
q esc close (esc first clears a filter, focus or scrub if there is one)

Quick view (the bar dropdown): b builds or updates the sampler when prompted, o or Enter opens the cluster, esc closes, j/k or Up/Down scrolls.

How it works

A Rust sampler (sampler/) runs as a shell service, reads /proc, /sys and cgroup v2 every second (configurable), keeps 120 samples of history, and streams one JSON line per tick. The QML side only renders. Apps are systemd scopes, so accounting uses the kernel's own per cgroup counters: shared pages count once and short lived processes are not missed. Jobs are POSIX process groups on a terminal. Ports come from joining /proc/net/tcp with each process's socket inodes. GPU per App comes from DRM fdinfo.

The vocabulary lives in CONTEXT.md, the wire contract in docs/sampler-protocol.md.

Settings

All settings live on the widget's entry in ~/.config/omarchy/shell.json, under bar.layout.<section>. There is no settings dialog in the shell yet (the manifest's schema is what the marketplace and a future panel read), so set them from the terminal:

omarchy bar set ryanyogan.omatop overlaySeconds 3
omarchy bar set ryanyogan.omatop motionHz 20
omarchy bar set ryanyogan.omatop reducedMotion true

That edits shell.json and reloads the shell config; the plugin picks the change up live. Or edit the file by hand and run omarchy-shell shell reloadConfig:

{ "id": "ryanyogan.omatop", "overlaySeconds": 3, "motionHz": 20 }
Key Default Range What it does
refreshSeconds 1 1 to 30 Sampling interval outside the quick view. The open dropdown uses 0.5 seconds. Feeds the timelines and the bar glyph.
overlaySeconds 5 1 to 10 How often the cluster takes a reading: dials, trip computer, pressure, the list and its meters.
motionHz 30 0 to 60 Ceiling on the cluster's motion rate. 0 steps once per reading.
reducedMotion false Turns off every animation.
showPercent false Shows the CPU percentage next to the bar glyph.

Two cadences. The sampler takes a sample every second (refreshSeconds), or every half second while the quick view is open, and that feeds the timelines: the ledger keeps 120 samples and scrolls a step per sample, from the very first sample. The rest of the cluster takes a reading every five seconds (overlaySeconds): dials, trip computer, pressure line, the list and its meters all describe one instant, and glide to the next one. Nothing in the overlay jumps once a second. A faint accent line under the trip computer fills as the next reading approaches, and the footer says the cadence. Stopping, pausing or restarting something shows its consequence on the next sample rather than the next reading.

Motion rate. With the cluster open the needles, arcs, meters and timelines glide instead of stepping. One shared clock drives all of it; every frame the overlay draws costs the same (about 3.5 ms of CPU on a 5K display, whatever moves in it), so the frame rate is the whole price of that motion: 30 fps costs about 10% of one core while the cluster is open, 20 fps about 7%, 12 fps about 5%, stepping once per reading about 3.5%. The rate is picked for the machine: 30 fps with sixteen or more cores, 20 with eight to fifteen, 12 with four to seven, stepping below that, and it drops to stepping whenever the kernel reports heavy or critical Pressure, since that is exactly when there is no CPU to spare. The footer says which is in effect. motionHz is a ceiling on all of that; reducedMotion turns it off along with every other animation.

Measured on a Ryzen AI 9 HX 370 (see docs/performance.md, harness in docs/measure.py): with nothing open the plugin adds about half a percent of one core and 10 MiB; the sampler sends a ~3 KB tick each refresh while nothing is open (vitals, pressure and slim offender rows for the averages, with the fd scan paused) and only sends the full App list while a surface is looking at it. Earlier quick-view measurements were under 1% of one core; the v1.2.0 comparison records about 1.5% for the whole shell with either dropdown. The cluster costs about 7% of one core at 20 fps and about 10% at the default 30, in every mode: it used to triple to 11% while you typed a search or while the machine was under critical pressure, because two looping animations pinned the window to the display's refresh rate.

The v1.2.1 dropdown uses fixed dot geometry and one short-lived fade clock. On this 5K desktop, a short empty-workspace comparison measured 7.00% whole-device GPU busy with shared-clock fades versus 11.62% with the initial per-cell animation implementation. This is a comparison within the new design, not a claim of savings over v1.2.0. See the GPU investigation for the desktop baseline, method and limits, and the dropdown release review for validation.

License

MIT