Omahub
← All plugins
C

Snitch

by chris

Live per-app outbound connection map with country flags and one-key per-app firewall blocks.

Security review

Review recommended · 7 findings

Deterministic scan — not a security guarantee

Medium
Risk level
Medium
Analyzed commit
147efa6
Scanned
1 month ago
  • medium package_manager …/workflows/build.yml:30

    System package manager operation.

    apt-get install -y musl-tools
  • medium package_manager …/workflows/release.yml:35

    System package manager operation.

    apt-get install -y musl-tools
  • Command runs with sudo, elevating the process beyond the plugin environment.

    sudo apt-get install -y musl-tools
  • Command runs with sudo, elevating the process beyond the plugin environment.

    sudo apt-get install -y musl-tools
  • Command runs with sudo, elevating the process beyond the plugin environment.

    sudo will ask for your password)…"
  • Command runs with sudo, elevating the process beyond the plugin environment.

    sudo sets `SUDO_UID`.
  • Docs sudo README.md:47

    Command runs with sudo, elevating the process beyond the plugin environment.

    sudo pacman -S nftables conntrack-tools

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
147efa6
Reviewed
1 month ago

The plugin is a network monitor with optional per-app blocking via nftables/cgroup. The deterministic scan flagged package-manager and sudo usage, but these are legitimate: CI installs build dependencies, and the README/scripts use sudo/pkexec only for the optional privileged helper installation and required packages, all clearly documented and user-initiated. No obfuscation, hidden persistence, or destructive behavior was found; the code is transparent and well-structured.

  • The privileged helper installation (scripts/install-privileged.sh) uses sudo/pkexec to copy files to /usr/lib/snitch and install a polkit policy; this is expected for firewall functionality but requires user consent and is clearly documented.
  • The build.sh script may invoke `omarchy pkg add rust` if cargo is missing, which is a package manager operation but within the plugin's own environment and not a system-wide change without user action.
  • The plugin writes to ~/.config/hypr/bindings.lua only when the user explicitly clicks 'Set hotkey', which is opt-in and not automatic.
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/ccdwyer/omarchy-snitch --enable
System #bar #quickshell #security

Snitch

Live per-app outbound connection map for the Omarchy shell: country flags, animated arcs, and one-key per-app firewall blocks.

Coverage is TCP + connected UDP. /proc/net/udp only has a remote endpoint for connect()ed sockets, so most QUIC/HTTP-3 is invisible or listed as UDP (unresolved) and never drawn as an arc. Reverse-DNS is off. Nothing leaves the machine except the traffic that was already leaving.

Install

omarchy plugin add https://github.com/ccdwyer/omarchy-snitch.git --enable
cd ~/.config/omarchy/plugins/io.github.chris.snitch
./build.sh

This repository does not contain Linux binaries. The authoring host is macOS and cannot emit trustworthy musl/ELF images. First install builds snitchd and snitch-block from source with one command (./build.sh, typically seconds with a warm Cargo cache).

After a git tag, GitHub Actions (.github/workflows/release.yml) publishes x86_64-unknown-linux-musl and aarch64-unknown-linux-musl binaries plus SHA256SUMS-*. You can download those release artifacts instead of compiling:

# example once a v1.x tag exists
arch=x86_64-unknown-linux-musl
ver=v1.0.0
base="https://github.com/<owner>/<repo>/releases/download/${ver}"
curl -LO "${base}/snitchd-${arch}"
curl -LO "${base}/snitch-block-${arch}"
curl -LO "${base}/SHA256SUMS-${arch}"
sha256sum -c "SHA256SUMS-${arch}"
mkdir -p bin
install -m 0755 "snitchd-${arch}" bin/snitchd
install -m 0755 "snitch-block-${arch}" bin/snitch-block

omarchy plugin add copies files only — it never compiles or installs polkit policy. Monitoring works with zero privilege once snitchd is on PATH or ./bin/snitchd. It does not need nftables, conntrack, or cgroup v2.

Required packages (blocking only)

Blocking additionally needs:

Need Binary / check Package (Arch) Package (Debian/Ubuntu)
nftables nft nftables nftables
conntrack conntrack conntrack-tools conntrack-tools
cgroup v2 /sys/fs/cgroup/cgroup.controllers kernel unified hierarchy (systemd default) same
# Omarchy / Arch
sudo pacman -S nftables conntrack-tools

snitch-block status reports nft, conntrack, cgroupv2, and blockingReady. If any are missing, the UI greys out block controls and shows the reason plus the package names. The map keeps running.

Blocking needs the helper installed at the path the polkit policy authorizes. Open the Snitch panel from the bar and click Install helper — that opens a terminal in the plugin directory, builds snitch-block if needed, and runs scripts/install-privileged.sh. Enter your password in the polkit prompt (setup may ask twice: copy files, then authorize the helper). After that, blocks should be prompt-free.

The same panel has Build snitchd when the daemon is missing and Install packages when nftables / conntrack-tools are missing.

You can still run the script by hand from the plugin directory:

cd ~/.config/omarchy/plugins/io.github.chris.snitch
./scripts/install-privileged.sh

That script copies the helper with pkexec /usr/bin/install (not pkexec bash), then runs pkexec /usr/lib/snitch/snitch-block status so the path-scoped helper action is what auth_admin_keep caches. Do not use sudo.

Place the pill if it did not land on the bar:

omarchy bar move io.github.chris.snitch --section right

Usage

Click the bar pill (live connection count) to open the panel. If no hotkey is set, the bar and panel offer Set hotkey (suggested Super+Alt+S). That bind is written to ~/.config/hypr/bindings.lua only when you click it — never on first load.

  • Quiet pill — count of live conversations
  • Amber pulse — a network prefix never seen before (/24 v4, /48 v6)
  • Red outline — at least one app is blocked
  • Away digest on open: N new networks while you were away

Hover an arc: firefox → 142.250.x.x, US, port 443.

Keybinds (panel focused)

Key Action
↑ ↓ / k j Move the app list
b / Enter Block or unblock the selected app
i Install helper, build snitchd, or install nftables (when shown)
/ Search apps, IPs, countries
Esc Close search, confirm dialog, or panel

The supported way to open the panel is clicking the bar pill (or the bar-widget open()/toggle() the shell routes to that widget). Opt-in Super+Alt+S (or Super+Shift+Alt+S if that combo is taken) runs the same toggle. The manifest also declares a panel kind so shell summon|hide|toggle|call are valid IPC verbs:

omarchy-shell shell summon io.github.chris.snitch '{}'
omarchy-shell shell hide io.github.chris.snitch
omarchy-shell shell toggle io.github.chris.snitch '{}'
omarchy-shell shell call io.github.chris.snitch ping '{}'

shell call … ping works when the panel is loaded (keepLoaded). The reliable service path is the keep-loaded IpcHandler (always pass the string argument):

omarchy-shell io.github.chris.snitch ping ''
omarchy-shell io.github.chris.snitch status ''

Summoned placement is best-effort (KeyboardPanel centerOnBar when the host provides shell.bar). That host field is not in the Quattro IPC table; if it is missing, use the pill.

Blocking

Blocking uses nftables via polkit. Monitoring does not. Production blocks always run pkexec /usr/lib/snitch/snitch-block — checkout copies are never authorized.

Every PID passed to block-app is checked against the invoking user (PKEXEC_UID, then SUDO_UID — never the helper's root euid). The process must exist, its UID must match that user, and starttime must still identify the same PID. PIDs owned by other users, PID 1, kernel threads, and missing PIDs are refused; the helper does not migrate or firewall them.

block-app creates /sys/fs/cgroup/snitch.slice/snitch-<app>/, migrates the app's process tree into it, installs

socket cgroupv2 level N "snitch.slice/snitch-<app>" drop

on table inet snitch (N is the path-component count, 2 for the default layout), migrates every live PID in the validated forest (including same-UID helpers in a private app-*.scope), and flushes conntrack for TCP and connected UDP remotes. verified: true is returned only when every live PID is in the snitch cgroup, the nft rule lists, and conntrack deletion succeeded. Processes already in the root cgroup are refused (endpoint fallback is offered). Partial migration rolls back.

If cgroup migration or the match fails, the UI offers endpoints only — affects all apps and will not silently substitute block-ips. Endpoint fallback records per-app ownership; Unblock calls unblock-ips so that app's addresses leave the set (shared addresses owned by another blocked app stay). system and unknown rows cannot be blocked.

If no polkit authentication agent is present, /usr/lib/snitch/snitch-block is missing, or nft / conntrack / cgroup v2 is unavailable, block controls are greyed and the panel shows the reason. Monitoring still works.

snitch-block teardown deletes table inet snitch and the plugin's cgroups and reports failure if either remains. Other firewall tables are never touched.

Honest limitations

  • Sub-second flows can slip between 500ms samples. The seen-set is advisory.
  • TIME_WAIT and LISTEN sockets are parsed then dropped; they are not conversations.
  • Other-uid processes appear as system (no desktop identity without privilege) and cannot be blocked.
  • UDP remotes via conntrack are a documented v1.1 path, not a 1.0 claim.
  • Linux prebuilts are not committed. First install is ./build.sh. Tagged GitHub Releases from CI carry musl binaries and checksums.
  • City-level geo is out of scope. Centroids are country-level.

Replay / fallback

./bin/snitchd --replay tests/fixtures/replay.ndjson --socket "$XDG_RUNTIME_DIR/snitch.sock"

If snitchd is missing, the service plays data/replay.ndjson inside QML so the map still opens.

Dev

./tests/run.sh          # cargo test + node model tests + snitchd --self-test
cargo test --workspace

QML hot-reloads from ~/.config/omarchy/plugins/io.github.chris.snitch/.

Attribution

  • IP geolocation by DB-IP Country Lite, CC-BY-4.0.
  • Landmass: Natural Earth 110m, public domain.
  • Flags: bundled geometric SVGs (no emoji-font dependence); ISO code fallback.

Remove

omarchy plugin remove io.github.chris.snitch
pkexec /usr/lib/snitch/snitch-block teardown
pkexec rm -f /usr/lib/snitch/snitch-block /usr/share/polkit-1/actions/io.github.chris.snitch.policy
# If you opted in to Super+Alt+S, remove the marked bind block:
sed -i '/-- BEGIN io.github.chris.snitch/,/-- END io.github.chris.snitch/d' ~/.config/hypr/bindings.lua