Commit Graph

277 Commits

Author SHA1 Message Date
henrygd
a042e19549 wifi(linux): query associated station directly
mdlayher/wifi only dumps stations, which some full-MAC drivers (e.g.
out-of-tree Realtek USB) answer with an empty list, leaving RSSI
unavailable. Request the associated BSSID's station directly, as
`iw link` does, and parse it with native.ParseStationInfo.
2026-09-25 19:59:18 -04:00
henrygd
24792aa24f wifi: omit empty info snapshot and tidy up
- Info.WiFi uses json "wf" with omitempty so systems without Wi-Fi no
  longer store/broadcast "wifi":null
- move osascript exec into wifi_darwin.go; drop unused commandRunner
- share strongest-connection logic between table cell and sorting
- trim agent/wifi README
2026-09-25 18:43:05 -04:00
henrygd
16e3fbadce store compact RSSI stats and skip real-time collection
Stats.WiFi is now map[string]int8 (json "wf") holding only available
RSSI
readings; SSID and unavailable signals stay in Info.WiFi. Averages are
rounded to whole dBm.

Wi-Fi is collected only on the default 60s interval; real-time requests
reuse the last snapshot to avoid spawning osascript / dumping the BSS
cache every second.
2026-09-25 18:02:34 -04:00
Vito Cappello
86ab0fae8b feat: monitor connected Wi-Fi signal per interface (#2367) 2026-09-25 17:43:57 -04:00
henrygd
7bea20e3b6 refactor(agent): remove unused FsStats.Time
The disk I/O seed now reads from diskBaseline, so nothing reads
FsStats.Time anymore.
2026-09-25 13:50:19 -04:00
Ludwig J. Marx
433b83800f fix(agent): measure first disk I/O sample of an interval from the full baseline (#2420) 2026-09-25 13:38:04 -04:00
henrygd
3dfe062ee4 fix(agent): promote existing entry when root key is already registered
When the root drive is also listed in EXTRA_FILESYSTEMS, its key is
taken before addPartitionRootFs runs. Returning false there sent the
agent to addLastResortRootFs, which picks the most active device and
could register a different drive as root, overwriting that drive's
extra entry. addPartitionRootFs has already resolved the root device,
so promote the existing entry to root instead.
2026-09-25 13:24:11 -04:00
henrygd
3fb97b800c fix(agent): fall back to last-resort root when root key is taken
addPartitionRootFs reported success even when addFsStat skipped the
registration because the I/O key was already in use, e.g. when the root
drive is also listed in EXTRA_FILESYSTEMS. hasRoot was then set without
any root filesystem, and the last-resort fallback never ran. addFsStat
now reports whether it registered the filesystem and addPartitionRootFs
propagates that.

Also uppercase the drive letter in windowsVolumeName so "c:" and "C:"
normalize to the same key.
2026-09-25 13:15:14 -04:00
yi111
d708def38f fix(agent): keep Windows volume names intact when matching I/O devices (#2419)
filepath.Base is platform-dependent for a bare volume name: on Windows it
strips the "C:" specifier and returns "\", so every drive letter
normalizes to the same key. findIoDevice's exact match then returns
whichever disk.IOCounters entry the map yielded first, which registers the
root filesystem under a random drive. With EXTRA_FILESYSTEMS=D:,P: the
root disk disappears from the hub, and without it the root can be
registered under another drive's I/O device.

Normalize a bare volume specifier before taking a path base, and stop
taking a path base of the device in addPartitionRootFs, where it mangled
the value before findIoDevice could see it. registerFilesystemStats
already avoided this by only taking a base when the agent is not on
Windows.

Fixes #2417
2026-09-25 13:09:11 -04:00
henrygd
f50fb4f8e5 fix(agent): fix pacman update check under systemd sandbox
- store checkupdates' private DB in the agent data dir, since
  ProtectSystem=strict makes /tmp read-only
- sync the private DB every 12h and use `checkupdates -n` in between,
  forcing a sync when the DB is missing so -n never reports a false 0
- set WaitDelay so a timed-out command can't hang on child processes
2026-09-25 12:21:15 -04:00
Anand Hegde
c25408651f feat: show number of pending package updates (#2357) 2026-09-25 12:13:37 -04:00
Sven van Ginkel
f7528a0208 feat: Add option to define which DNS server a DNS monitor queries (#2389) 2026-09-24 19:01:10 -04:00
Ludwig J. Marx
b5ef015451 fix(agent): handle 32-bit wrap of disk I/O time counters (#2407) 2026-09-24 17:39:53 -04:00
Miłosz Kolber
6141b15f03 fix(agent): SKIP_GPU excludes GPU hwmon from temperatures and fans (#2313) 2026-09-24 13:59:52 -04:00
Sven van Ginkel
a99fe5e997 feat: add TLS certificate expiry check to HTTPS network monitors (#2401)
Co-authored-by: henrygd <hank@henrygd.me>
2026-09-23 17:07:29 -04:00
henrygd
8bf6917fe0 fix: prevent WebSocket reconnect loops on slow agent collections (#2294)
The hub waited only 5s (the request manager default) for stats over
WebSocket and closed the connection on any error. On hosts where
`zpool list` stalls (seen on Proxmox, up to its 10s timeout), collection
exceeded that limit, so the hub sent a close (code 1000) and the agent
reconnected. The refresh ran every other cycle, which caused a
disconnect roughly every 2 minutes.

Hub:
- Wait up to 30s for WebSocket stats responses.
- Keep the connection open when a stats request times out; only close
  it (and fall back to SSH) for other errors.

Agent:
- After the first collection, refresh `zpool list` pool stats and
  `zfs list` dataset usage in the background and serve cached values
  meanwhile, so a hung utility cannot delay the stats response.
2026-09-23 11:21:51 -04:00
henrygd
1aaabfc255 fix(agent): check all image repository digests (#2393) 2026-09-22 18:00:13 -04:00
henrygd
97ea3c16cb add custom user agent to HTTP monitors 2026-09-21 19:53:53 -04:00
henrygd
cbe4824ac3 add env var to disable container image update checks (#2371) 2026-09-21 10:49:53 -04:00
henrygd
2784460621 fix(agent): avoid RAID health warnings during healthy scrubs (#2109) 2026-09-18 21:05:49 -04:00
hank
bb1b39928e feat: network monitoring from agents (#2266, #1911)
Co-authored-by: Sven van Ginkel <svenvanginkel@icloud.com>
Co-authored-by: xiaomiku01 <xiaomiku01@outlook.com>
2026-09-18 13:22:50 -04:00
henrygd
18f7a4bbc0 agent: revert #2275 warning on certain SMART attributes (#2296, #2308, #2347) 2026-09-17 11:48:01 -04:00
Santhi Prakash
982101743e fix(zfs): skip zpool list when /dev/zfs unavailable in Linux (#2325)
Co-authored-by: henrygd <hank@henrygd.me>
2026-09-16 13:28:47 -04:00
Bruno Bousquet
f204dc17e6 feat(agent): Add docker image update available flag (#2211)
Co-authored-by: henrygd <hank@henrygd.me>
2026-09-10 19:39:40 -04:00
Alec Rubin
5fe1583655 fix(agent): don't warn about unset HUB_URL in SSH-only mode (#2316) 2026-09-10 16:58:12 -04:00
Ani Betts
8d6a5d5f6e feat(agent): report btrfs filesystems as storage pools (#2315)
Co-authored-by: henrygd <hank@henrygd.me>
2026-09-09 19:53:39 -04:00
Ryan Chou
027d0c204d fix(agent): extend WebSocket deadline for slow collections (#2297)
The agent resets its WebSocket deadline to 70s, but the hub's default
collection interval is 60s, so a single slow collection cycle is enough to
trip the deadline and start a reconnect loop even though the hub is still
serving the agent. Raise the deadline to 120s and add a regression test that
keeps the slow-collection window from being lowered below two minutes.

Verified with go test -tags=testing ./agent (focused tests and the full agent
suite minus the container-only TestDirectoryIsWritable case), go vet, the
agent build and gofmt.

Closes #2294
2026-09-05 13:09:25 -04:00
henrygd
bc21da9cb3 fix(agent): prevent possible deadlock when stopping SSH server (#2280) 2026-09-02 20:31:38 -04:00
hank
f104f31ee3 Merge commit from fork 2026-09-02 13:55:54 -04:00
Michał Mleczko
5969d36856 feat(alerts): add container health alerts with log excerpt on notifications (#2225)
Add a new "ContainerHealth" alert type that fires when a Docker container's
health check reports unhealthy, and resolves when it recovers. This mirrors
the existing Status (up/down) alert pattern: an alert can be armed per system
and honors the "min minutes" delay before firing.

When the alert fires, the notification (email and any configured webhook,
including Discord via shoutrrr) includes a log excerpt fetched live from the
agent for up to 2 of the unhealthy containers, prioritizing lines containing
"error" or "fatal" (falling back to the log tail if none match), capped to
keep the message well under Discord's size limit.

---------

Co-authored-by: hank <hank@henrygd.me>
2026-09-02 12:46:23 -04:00
Martin Stenröse
097180e8d7 feat(alerts): add alert for failed systemd services (#2173)
Adds a user-configurable "Failed Services" alert that notifies when any
tracked systemd service enters the failed state, and again when all services
recover.

---------

Signed-off-by: Martin Stenröse <martin@stenrose.se>
Co-authored-by: henrygd <hank@henrygd.me>
2026-09-01 20:41:48 -04:00
Tamás Vince
917d069ab3 feat: add ZFS monitoring (#2209)
- track pool capacity, health, I/O, scrub status, and vdev errors
- report dataset usage and correct ZFS filesystem metrics
- add pool charts, detail views, refresh controls, and health alerts
- persist pool details and include ZFS usage in disk alerts
- support configurable detail intervals and legacy agent compatibility

---------

Co-authored-by: hank <hank@henrygd.me>
2026-09-01 12:19:36 -04:00
Sven van Ginkel
b38fb7dafa feat: Add cumulative disk read/write totals to Disk I/O sheet (#2179) 2026-08-30 15:44:18 -04:00
Sven van Ginkel
87620f3251 feat(hub/agent): alphabetical disk ordering and root disk renaming (#2006) 2026-08-30 13:09:04 -04:00
Aditya Raj Singh
fa9de55433 fix(agent): warn on critical ATA SMART attributes (#2275) 2026-08-30 11:23:52 -04:00
Aditya Raj Singh
7c60f02802 fix(agent): don't read host CPU and memory totals from a Docker VM (#2272)
refreshSystemDetails() takes NCPU and MemTotal from the Docker daemon's
/info response. That only describes this machine when the daemon shares its
kernel. On macOS and Windows Docker runs inside a Linux VM, so the system
details header shows the VM's memory as the host total, and the VM's CPU
count clamps both cores and threads through the lxc branch below it.

Only consult Docker's host info on platforms where the daemon runs natively.
Everything else already falls back to gopsutil, which reads this host.
2026-08-30 11:09:39 -04:00
Ryan Chou
6fe268e463 fix(agent): read TOKEN_FILE like KEY_FILE instead of sending the whole file (#2276) 2026-08-30 10:16:43 -04:00
henrygd
0ad707288a fix windows sensor mocks and data directory tests 2026-08-26 11:50:19 -04:00
Aditya Raj Singh
0bc5470f08 fix(agent): count swap cache as used space (#2267)
SwapCached pages have been read back into memory but still occupy allocated swap slots. Subtracting them from SwapTotal - SwapFree underreported swap usage compared with free, Glances, and gopsutil's canonical SwapMemory metric.
2026-08-26 11:42:40 -04:00
Luke Wass
4c48fe0c41 fix(agent): carry Intel GPU averages forward between samples (#2256)
Intel GPUs (intel_gpu_top) never report temperature or memory, so the
"suspended card" heuristic in calculateGPUAverage (temp == 0 &&
memoryUsed == 0) fired on every collection that landed between samples.

intel_gpu_top samples every 3.3s (intelGpuStatsInterval) while the hub's
realtime worker collects every 1s, so most realtime collections had no
new sample (delta count 0) and returned an empty GPUData with power
omitted (json "p"/"pp" are omitempty). The frontend derives the GPU
Power Draw series and legend from the latest sample, so the chart and
legend blanked on roughly two of every three or four one-second cycles.

NVIDIA/AMD were unaffected because they report temperature even when
idle, so the heuristic never fired and the last average was already
carried forward.

Gate the zero-return on non-engine (discrete) GPUs so Intel GPUs carry
the last average forward during between-sample gaps, matching the
existing NVIDIA/AMD behavior. Add a regression test.
2026-08-24 10:33:00 -04:00
Aditya Raj Singh
f1e5797c76 fix(agent): round load average to two decimals (#2245)
Every other metric in getSystemStats is stored through utils.TwoDecimals.
The load averages were assigned straight from gopsutil, so whatever the
platform reported was recorded verbatim.

On Linux that goes unnoticed because /proc/loadavg is already two decimal
places. Everywhere else it is not. macOS and BSD divide a fixed point
value by fscale and produce numbers like 2.55322265625, and the Windows
implementation synthesises the average as a decaying EWMA over the
processor queue length counter, so an idle machine reports values like
1.3667392689044936e-73 instead of 0.

The hub already treats two decimals as the canonical precision for this
field, since records.go rounds the load average when it averages records.
That left the raw agent records as the only place carrying full precision.
2026-08-21 17:30:19 -04:00
Toomore Chiang
aa1d67a122 fix(agent): strip invalid UTF-8 from battery names (#2241)
Battery names come from firmware (sysfs model_name on Linux), which does not
guarantee valid UTF-8. The hub decodes agent payloads using the default
fxamacker/cbor decode mode, which rejects invalid UTF-8, so a single bad byte
in a battery name makes the hub drop the entire payload and mark the system
down until the agent is downgraded.
2026-08-19 11:02:33 -04:00
Alec Rubin
68a3f8962a fix(agent): read /proc/uptime on linux instead of sysinfo(2) (#2180)
gopsutil's host.Uptime() calls the sysinfo(2) syscall. Inside an LXC
container lxcfs virtualizes /proc/uptime but cannot intercept a
syscall, so every container reported the host's uptime.

Reads /proc/uptime on linux and falls back to host.Uptime() if the file
is missing or unparseable, so other platforms are unchanged.
2026-08-18 15:18:33 -04:00
Ilya Muratov
0eb3426619 fix(agent): discover fans on legacy hwmon parent devices (#2238) 2026-08-18 11:32:52 -04:00
Jan Dziąsło
96beadc8c9 fix(agent): add fallback for CPU model detection on MIPS architectures (#2138)
gopsutil's cpu.Info() does not parse the 'cpu model' field from
/proc/cpuinfo, which is the only source of CPU model names on MIPS.
Add a fallback that reads /proc/cpuinfo directly and combines
'cpu model' (e.g. 'MIPS 1004Kc V2.15') with 'system type'
(e.g. 'MediaTek MT7621 ver:1 eco:3') for a complete identifier.

The fallback only triggers when gopsutil returns an empty ModelName,
so x86/ARM/other architectures are unaffected.
2026-08-18 10:34:41 -04:00
Sven van Ginkel
65a6f60304 fix(agent): fix QNAP MD RAID arrays incorrectly reported as FAILED (#2065) 2026-08-18 10:10:12 -04:00
Pavel Artsishevsky
2df1f722e4 fix(agent): use time-based CPU calc for Podman containers (#2131) 2026-08-17 17:08:57 -04:00
henrygd
89ad51d4ce fix(agent): distinguish SMART devices sharing the same path
Co-authored-by: Digital <github@crni.xyz>
2026-08-17 13:55:27 -04:00
henrygd
ae037b278e agent: simplify FreeBSD temperature sensor names 2026-08-16 21:16:12 -04:00
QuantumFlux21
9f1128933f feat: Add support for reading CPU (dev.cpu.*.temperature) and ACPI thermal zone (hw.acpi.thermal.tz*) temperature sensors on FreeBSD systems. (#2227)
Co-authored-by: roib <roib@elsec.us>
2026-08-16 20:58:41 -04:00