syswatch 0.9.0

Single-host, read-only system diagnostics TUI. Twelve tabs covering CPU, memory, disks, processes, GPU, power, services, network, plus a Timeline scrubber and an Insights anomaly engine. Sibling to netwatch.
syswatch-0.9.0 is not a library.

What it shows

# Tab Replaces
1 Overview dashboard view of all subsystems
2 CPU htop CPU panel, top -d, mpstat
3 Memory free, vm_stat, htop mem panel
4 Disks iostat, iotop (aggregate)
5 Filesystems df -h, df -i, mount
6 Procs htop, ps auxf, pstree
7 GPU ioreg AGXAccelerator PerformanceStatistics / /sys/class/drm
8 Power pmset, ioreg AppleSmartBattery / /sys/class/power_supply
9 Services launchctl list / systemctl list-units
0 Net nettop, iftop
- Timeline (no equivalent — session log + scrubber)
+ Insights (no equivalent — plain-English anomaly cards)

Where htop shows you what's running, SysWatch shows you what's happening — across CPU, memory, IO, GPU, power, services — and tells you why in plain English when something's anomalous.

Install

brew install syswatch                 # macOS / Linux
nix-shell -p syswatch                 # NixOS / Nix
paru -S syswatch                      # Arch
cargo install syswatch                # anywhere with Rust

Or grab a pre-built binary from Releases.

# From source
git clone https://github.com/matthart1983/syswatch.git && cd syswatch
cargo build --release && ./target/release/syswatch

The Nix and Arch packages are maintained by community packagers — thank you. File packaging issues with them; file syswatch bugs here. The Repology page shows which packages are current.

Prerequisites (source/cargo builds): Rust 1.75+. No system dependencies on Linux. macOS links against the system frameworks.

Usage

syswatch                       # default 1Hz tick
syswatch --tick 500            # 2Hz
syswatch --tab procs           # boot straight into a tab
syswatch --replay session.swr  # scrub a recorded session
syswatch --lite                # the one-screen Lite view
syswatch --dense               # the high-density Dense view

Keys

1 2 3 4 5 6 7 8 9   →  Overview / CPU / Mem / Disks / FS / Procs / GPU / Power / Services
0 - +               →  Net / Timeline / Insights
Tab / Shift-Tab     →  Cycle tabs
↑ / ↓               →  Select row (Procs, Services)
s                   →  Cycle sort (Procs, Services)
/ or f              →  Filter the table (Procs, Memory, Services)
← / →               →  Scrub session backward / forward
Home / End          →  Oldest sample / live
p                   →  Pause
g                   →  Graph style (bars / dots)
t                   →  Cycle theme (incl. "terminal" — uses your terminal's own palette)
,                   →  Settings (tick, theme, btop-style fade)
S / R               →  Snapshot to disk / record session
V                   →  Cycle views: Full → Lite → Dense
L                   →  Jump straight to the Lite view
?                   →  Help
q / Ctrl-C          →  Quit

Lite view

syswatch --lite, or L at any time. One screen at 80×24 answering one question — why is this machine hot, slow, or loud? — with six keys and four colors. It is not the full tool with tabs hidden; it is a different product for someone with one machine, and the deliberate sibling of netwatch --lite: identical grid geometry, column positions, keys and palette, so muscle memory carries between them.

q  quit     p  pause    /  filter (name or user)
↵  detail   L  full     ?  help          ↑↓ / j k move   Esc unwind

Recorded with vhs demo-lite.tape in the btop-style look — braille area plots (g) over the faint dot grid, with the right-bright / left-dim gradient — at --tick 250 so the charts fill inside a GIF. They hold one sample per column and fill from the right in real time, so at the 1 Hz default the 78-column chart takes 78 seconds. Nothing is fast-forwarded: the axis label measures the history it is actually showing, and the sparkline header reports its own span, so both say what the faster tick did.

CPU gets a three-row chart and memory two — when a machine feels wrong, CPU is the answer more often than RAM. A single vitals line carries temp, fan, power and disk throughput, each rendering -- rather than moving when a sensor isn't readable. Red appears only when the machine is actually in trouble — thermal throttling, swap thrashing, critical memory pressure — on fixed thresholds with hysteresis (three samples to fire, five to clear) so it never flaps. Memory pressure comes from the kernel's own verdict where there is one (PSI on Linux, kern.memorystatus_vm_pressure_level on macOS) rather than being inferred from swap, so a Mac doing what Macs normally do doesn't read as an emergency. It follows your theme and graph style like every other screen, and it is read-only, same as the rest of syswatch.

Dense view

syswatch --dense, or V to cycle Full → Lite → Dense. Every subsystem on one 130×44 screen. Where Lite is the smallest useful thing, Dense is the largest: six boxes tiling the terminal with zero chrome rows — no header, no tab bar, no status bar. Identity, uptime, aggregate, sort state, page range and every keybind live inside the box borders, so a heading costs no row.

rows  0-11  cpu            full-height braille graph · axis · vitals
rows 12-23  mem  │ net     composition + history │ mirrored down/up
rows 24-31  cores │ disk   per-core grid │ read/write sparklines
rows 32-43  procs          detail-in-place + process table

The mirror means "two directions of one flow." Only net earns one: download grows up from a shared axis, upload grows down, so traffic symmetry becomes a shape — a restore is a cliff above the line, a backup a cliff below it. CPU and memory have no opposing direction, so each gets one honest full-height graph rather than a manufactured partner. Temperature is a bounded scalar, not a flow, so it sits on the vitals row with a green→red meter.

Colour carries magnitude rather than identity: every cell is coloured by its own height in the plot, so you see a spike's severity before you read the axis. That split is deliberate — throughput graphs ramp cool→bright because a saturated disk during a backup is working, and only bounded values where high genuinely is bad (temperature, memory pressure, disk saturation) get the green→amber→red vocabulary. Ramps are built from your theme, never hardcoded; on the 16-colour terminal theme they step through the palette you already have rather than synthesising colours you never chose.

16 zoom a box to the whole frame — the process table at forty rows, or just the network mirror — and esc restores the grid. Below 100×38 it falls back to a three-box compact arrangement rather than cramming.

Every number is measured over the window you can actually see, so a printed peak is never one hiding in scrolled-off history. Graphs hold one sample per column and fill from the right in real time, same as Lite: at the 1 Hz default a 120-column graph shows two minutes and takes two minutes to fill. The axis says what it is actually showing.

It is the sibling of netwatch's Dense view — same primitives, same panel idiom, same V cycle — so muscle memory carries between them the way it already does for Lite.

What's distinctive

Insights tab. Heuristic anomaly detection over the rolling session — swap thrash, runaway processes, disk full, memory pressure, high load, zombie parties — surfaced as plain-English cards with a suggested tab. The Overview's bottom strip and the tab bar's [+] badge keep them in sight from anywhere.

Session-wide scrubbing. The Timeline tab's ←/→ rewinds the entire app — every panel transparently shows historical state. R records a session to a .swr file; --replay scrubs it back later. S dumps the current snapshot to disk.

Honest about platform limits. Where data needs sudo (powermetrics for fans, per-component power, GPU util on Apple Silicon) the tab shows what we can get for free and a one-line note about what's gated. Nothing is faked, nothing prompts.

Anti-goals

  • Not multi-host. For fleet view, use NetWatch's web dashboard.
  • Not a daemon. No long-running collector, no Prometheus push. The session is the database.
  • Not interactive remediation. Read-only, deliberately. We don't kill, renice, unmount, or restart.
  • Not a logging product. We surface OOM kills as a signal in Memory; we are not a log search UI.
  • Not pretty charts for screenshots. Block sparklines, real numbers, no smooth curves, no themes-of-the-week.

Scope

All twelve tabs render real data on macOS and Linux. Cross-platform collection via sysinfo; aggregate disk IO routes through netwatch-sdk so SysWatch and the NetWatch agent share a single source of truth. Recording/Replay (R / --replay), Settings (,), Help (?), table filter (/ or f, on Procs / Memory / Services), themes (t), the Lite view (L / --lite), the Dense view (V / --dense), and the btop-style fade rendering are all live.

Lite's temp / fan / power vitals depend on platform sensors: Linux reads /sys/class/hwmon, /sys/class/thermal and RAPL; macOS needs IOKit/SMC access, so on Apple Silicon those three commonly render -- while CPU, memory, disk and processes remain fully live.

No sudo, ever. GPU utilization, VRAM, and the renderer/tiler split on Apple Silicon come from ioreg (AGXAccelerator PerformanceStatistics); GPU temperature, per-rail power, and fans come from IOReport + SMC. Linux reads sysfs (/sys/class/drm, thermal zones, hwmon). Where a figure genuinely needs elevated access, the tab says so rather than prompting.

Behind cargo features — NVIDIA live GPU stats (gpu-nvidia, nvml-wrapper).

Architecture

src/
├── main.rs              CLI + entry
├── app.rs               Event loop, tab state, scrub plumbing
├── collect/             One Collector per subsystem; Snapshot the wire format
│   ├── collector.rs     sysinfo-backed CPU/Mem/Procs/Net + dispatch
│   ├── gpu.rs           ioreg AGXAccelerator / sysfs DRM / nvml
│   ├── macos_sampler.rs Shared IOReport + SMC worker (GPU/power/fans)
│   ├── power.rs         ioreg / pmset / sysfs power_supply
│   ├── services.rs      launchctl / systemctl
│   └── ring.rs          Bounded history + nth_back for scrubbing
├── insights/            Pure functions over (History, &Snapshot)
├── tabs/                One file per tab; thin renderers over the model
└── ui/
    ├── chrome.rs        Header, tab bar, footer
    ├── palette.rs       Single source of color truth
    └── widgets.rs       block_bar, sparkline, panel

Refresh model: a 1 Hz fast loop reads CPU/Mem/Net/IO in-process every tick; the heavier collectors run on their own budgets — processes every ~1.5 s, Power/Services every 5 s, per-process bandwidth on a background thread — so the loop stays cheap regardless of tick rate. The UI redraws on tick or keypress.

License

MIT.