Skip to content

Releases: InvictusNavarchus/batmon

v0.8.3

Choose a tag to compare

@github-actions github-actions released this 03 Oct 22:56
v0.8.3
563dc53

Upgrading: databases migrate automatically on first start.

Added

  • Storage stall and I/O pressure telemetry in the flight recorder and
    historical databases (#16):
    • iowait_pct: CPU time spent waiting on disk I/O, separated from idle time
      in /proc/stat.
    • procs_blocked: count of processes currently waiting for I/O (nr_iowait).
    • dirty_kb: uncommitted dirty writeback memory from /proc/meminfo.
    • psi_io_some and psi_io_full: 10-second rolling averages from Linux
      Pressure Stall Information (/proc/pressure/io), falling back to None on
      kernels built without CONFIG_PSI.

v0.8.2

Choose a tag to compare

@InvictusNavarchus InvictusNavarchus released this 10 Sep 23:33
97e1340

Behaviour change: debug.db now keeps the last 6 hours of recording rather than the last 6 hours of wall-clock time. Downtime (power-off, suspend, unreadable battery) no longer ages data out, so the run-up to a crash survives a long power-off — but rows, including top_processes, can now be older than 6 hours.

What's Changed

Full Changelog: v0.8.1...v0.8.2

v0.8.1

Choose a tag to compare

@InvictusNavarchus InvictusNavarchus released this 07 Sep 10:07
29e1f67

This release introduces critical stability fixes for hardware telemetry handling—ensuring unreadable or transient sysfs reads are no longer interpreted as zero or false—and completes the codebase transition away from legacy TypeScript parity framing into self-documenting, native Rust patterns.


🚀 Key Highlights & Bug Fixes

1. Distinguish Absent / Unreadable Readings from Zero (#14)

Previously, several sysfs parsers mapped unreadable, empty, or transient driver attributes to 0.0 or false. This led to several failure modes that have now been resolved:

  • False Low-Battery & Health Alerts: An unreadable capacity or energy_full sysfs attribute no longer triggers a sudden false "CRITICAL: Battery Low" or "Battery health at 0.0%" alert.
  • Cycle Count Loss & Spikes:
    • Unreadable energy_full_design no longer resets months of accumulated battery cycle counts to zero; it now preserves the carried count.
    • Unreadable energy_now no longer fabricates false cycle accumulation deltas.
  • Thermal False Negatives: Empty hwmon sensor files are now treated as absent rather than 0 °C, preventing false clearing of active thermal alerts.
  • Tri-State Battery Presence: Sysfs present handling is now strictly tri-state (Some(true), Some(false), or None). Unreadable or malformed presence values are treated as unknown rather than false, ensuring active alert latches are not prematurely cleared on transient read errors.
  • Flight Recorder Retention Pruning: Buffer pruning has been decoupled from sample generation, ensuring the 6-hour retention window continues to be enforced even when a battery remains unreadable or absent.
  • Reduced Log Spam & Stale Estimates:
    • Unreadable sysfs states now warn once per episode rather than logging every tick (1 Hz).
    • Stale UPower runtime estimates are explicitly invalidated when telemetry stops reading, preventing reinserted batteries from inheriting outdated time-to-empty calculations.

2. Retiring JavaScript Parity Framing (#13)

With the migration from the previous TypeScript implementation complete, all legacy parity references, JS-centric naming conventions, and comparisons have been removed in favor of clean, self-contained Rust implementations:

  • Renamed parity Module to formats: The module now reflects its true purpose—handling kernel text parsing, number rounding, and metric formatting.
  • Semantic Function Naming:
    • round_js → round_half_up
    • to_fixed → format_decimals
    • js_number → parse_number
    • js_parse_int → parse_leading_int
  • Documentation & Test Accuracy: Cleaned up documentation across alerts, thermal checks, migrations, and /proc parsers to document actual behavior, IEEE 754 edge cases, and pinned test properties directly without relying on obsolete implementation comparisons.

🛠️ What Changed

Telemetry & Hardware Handling

  • src/telemetry/battery.rs: Made energy() and charge_pct() fallible (Option<Sample>); explicitly validate present attribute to distinguish 0, 1, and malformed/unreadable states.
  • src/telemetry/thermal.rs: Unified battery and hwmon attribute readers to discard empty sensor files instead of coercing to 0.
  • src/cycles.rs: Guaranteed accumulated cycle counts are preserved across unreadable driver ticks.
  • src/daemon.rs: Hoisted retention pruning to run on all ticks, including early-return no-op ticks.

Refactoring & Code Quality

  • src/formats.rs (formerly src/parity.rs): Renamed module and utility helpers to standard descriptive naming.
  • Updated test suites and doc comments across src/alerts/, src/migrations/, src/paths.rs, and src/telemetry/ to assert native invariant behaviors.

Full Changelog: https://github.com/InvictusNavarchus/repository/compare/cfc97c3...2a50ecb

v0.8.0

Choose a tag to compare

@InvictusNavarchus InvictusNavarchus released this 07 Sep 05:58
cfc97c3

This release introduces single-instance daemon enforcement via advisory file locks, a streamlined one-line installer with precompiled static musl binaries, and significant internal codebase decluttering through modularized unit tests.


🚀 What's New

🔒 Single-Instance Advisory Lock

  • Prevent Concurrent Runs: Enforced an exclusive, non-blocking advisory file lock (batmon.lock) on startup. Prevents multiple daemon instances from polling sensors or generating duplicate SQLite samples simultaneously (#12).
  • Helpful Collision Diagnostics: If an instance is already running, the new process identifies the existing PID, outputs troubleshooting information, and exits with a non-zero exit code so systemd properly honors Restart=on-failure.
  • Robust Error Handling: Added proper I/O error propagation when writing PIDs and support for bare/relative lock paths.

⚡ One-Line Zero-Config Installer & Release Pipeline

  • Curl-to-Bash Installer: Added a zero-dependency installation script (install.sh) supporting auto-detection of Linux CPU architectures (x86_64, aarch64), pulling precompiled binaries directly from GitHub Releases.
  • Safe Installation Lifecycles: The installer now cleanly shuts down any running batmon service before file replacement and verification to avoid SQLite lock contention and database races.
  • Automated musl Builds: Integrated a GitHub Actions release pipeline to build static musl binaries with accompanying SHA256 checksums published with each release.

🧹 Code Quality & Refactoring

Test Module Extraction (#11)

  • Extracted inline unit tests exceeding 100 lines across 16 core modules into dedicated child test modules (e.g., <module>/tests.rs).
  • Core engine logic across alerts, thermal, daemon, cycles, sampler, processes, db, migrations, proc, config, battery, paths, and dbus is now significantly more readable and maintainable while maintaining test accessibility to private items.

📖 Documentation

  • Quick Start: Promoted the copy-paste one-line installation command to the top of the README.md.
  • Test Architecture Guide: Documented the internal test layout convention and instructions for using git blame -w -C -C -C to trace history across extracted test files.

v0.7.0

Choose a tag to compare

@InvictusNavarchus InvictusNavarchus released this 07 Sep 03:23
79c4d4f

The Native Rust Port

This release replaces the entire TypeScript/Bun implementation with a native, single-binary Rust daemon.

The primary goal of this port was to eliminate runtime overhead and make batmon adhere strictly to the observer principle: measuring system state must not meaningfully perturb the system. The new binary drops Bun and Node dependencies entirely, talks directly to kernel interfaces and D-Bus, and preserves 100% data and schema parity with existing databases.


🚀 Performance & Resource Footprint

Empirically validated over extended multi-hour side-by-side differential runs across reboots:

  • Memory Footprint: Median resident memory dropped from 28.4 MB (Bun/TypeScript) to 6.6 MB (Rust)—a >4× reduction, with peak memory reduced by up to 5.7×.
  • Thread Count: Reduced from ~15 threads down to 6.
  • Zero Child Processes: Eliminated all subprocess execution (busctl and notify-send spawns), saving ~1,440 process forks per day.
  • Sensor Path Caching: Sensor resolution in /sys/class/hwmon is now cached after initial probe rather than re-traversed every second, cutting ~3.9 ms of syscall overhead per tick while retaining automatic rescan fallback if devices drop or drivers load late (e.g. k10temp).
  • Deadline-Scheduled Cadence: Replaced setInterval with deadline-based sleep (sleep_until). Over a typical day, this eliminates roughly 7.5 minutes of cumulative tick drift caused by interval sleep accumulating tick execution latency.

🔔 Native D-Bus Integration & Alerts

  • Direct D-Bus Communication: Interacts directly with the system and session buses using zbus, removing dependencies on external CLI utilities (busctl, notify-send).
  • Bubble Replacement (replaces_id): Notification alerts now track IDs per family and replace active notification bubbles rather than stacking duplicates on the desktop.
  • Non-Blocking UPower Queries: Added a 2-second timeout to all UPower property reads. If UPower hangs or stalls, the daemon falls back to internal arithmetic rather than blocking the sample loop.
  • Lazy Notification Bus Reconnection: If the daemon starts at boot before the desktop notification daemon is alive, it cleanly degrades to journal logging and periodically retries connecting without crashing or spamming the session bus.

🛡️ Data Integrity & Differential Parity

The port underwent exhaustive continuous differential testing running side-by-side against the TypeScript implementation on real hardware through charge, discharge, and full system shutdown/reboot cycles:

  • Bit-for-Bit Cycle Accuracy: Across 1,430+ paired observations across two boot sessions, the cycle integration matched the TypeScript engine down to 12 decimal places (2.065114525, delta: 0.000000000000).
  • Database & Schema Parity: Verified against 7 starting database states and live historical databases. Both the flight-recorder (samples) and downsampled historical databases migrate cleanly and contiguously.
  • Safe Carry-Forward: Fixed a legacy edge case where battery removal or pack swapping would integrate missing battery capacity as discharge. The cycle count is now safely carried forward without artificial wear accrual.
  • Strict Numeric & Formatting Parity: Implemented exact IEEE-754 / JavaScript toFixed and rounding parity helpers to ensure stored floating-point telemetry and alert thresholds never drift or diverge on negative ties.
  • NaN & Infinity Guards: Alerts and units now rigorously validate non-finite numbers—sensor anomalies reporting NaN or Infinities can no longer trigger erroneous 0% critical alarms or bypass threshold checks.

📦 Upgrading & Installation

Upgrading is completely seamless. Your existing databases will be continued without data loss.

  1. Install Prerequisites: You only need cargo at build time. No Bun, Node, or runtime dependencies are required on the host system.
  2. Run Installer:
    ./install.sh
    • Installs a single compiled static binary to ~/.local/bin/batmon.
    • Cleans up legacy TypeScript runtime sources from ~/.local/share/batmon.
    • Preserves existing flight-recorder and historical SQLite databases.
    • Updates the systemd user service (batmon.service).

🛠️ Developer & Tooling Changes

  • Build and release workflows now use cargo-release instead of release-it.
  • Comprehensive property test suites covering state machine latches, hysteresis boundaries, and cycle accrual monotonicity.
  • Full CLI integration suite testing signal handling (clean WAL checkpointing on SIGTERM) and one-shot execution.

v0.6.0

Choose a tag to compare

@InvictusNavarchus InvictusNavarchus released this 06 Sep 04:21
ab556d8

Release v0.6.0

This release introduces major architectural improvements to telemetry accuracy, battery cycle accounting math, forensic tracking across system boots, and alert resilience. It resolves critical edge cases in low-power cycle integration, eliminates false-positive alerts via stateful debouncing, improves database pruning performance, and optimizes filesystem operations on modern Linux distributions.


🌟 Highlights

  • Precision Cycle Accounting (Fixed Quantization Freeze): Previous accumulators rounded intermediate cycle deltas to 4 decimal places, which caused the cycle counter to completely freeze at low power draws (<10.8W on a 60Wh battery). Cycle accumulation now retains full IEEE 754 64-bit float precision in memory, writing high-precision estimations without loss.
  • Kernel Forensics (boot_id & Monotonic Uptime): Every sample now captures /proc/sys/kernel/random/boot_id and monotonic /proc/uptime. This prevents erroneous historical integrations across system reboots and cold boots.
  • Three-State Power Modeling: Refactored the telemetry layer from a naive charging/discharging boolean into an explicit three-state model (charging, discharging, unknown). Low and critical alerts are strictly gated to active discharging to eliminate spurious notifications while plugged into AC.
  • Load-Normalized Thermal Anomaly Alerting: Added an intelligent alert that triggers when CPU temperatures remain dangerously elevated despite minimal system load—acting as an early-warning diagnostic for cooling fan failures, dry thermal paste, or clogged heatsinks.
  • Alert Debounce & Hysteresis: Added a 3-tick consecutive threshold filter across all telemetry triggers to filter out instantaneous thermal spikes and electrical noise.
  • Btrfs CoW Optimizations: install.sh now configures the local SQLite directory with chattr +C (No-CoW) on Btrfs filesystems to prevent write amplification, fragmentation, and erratic WAL checkpoint latency.
  • Standalone Binary Target: Added standalone binary compilation via bun build --compile for direct deployment without requiring a system-wide Bun runtime.

🛠️ What's Changed

🔋 Telemetry & Hardware Forensics

  • Sysfs Battery Presence: Guarded battery detection by verifying the physical sysfs battery directory exists before reporting is_present, preventing phantom battery logs on AC-only desktops or disconnected batteries (0cfa587).
  • Environment Overrides: Added support for BATMON_POWER_SUPPLY_BASE and sanitized empty-string path fallbacks (b8ecc3e).
  • UPower Property Throttling: Added rate-limiting to D-Bus / busctl UPower queries to reduce IPC overhead and avoid blocking the 1-second flight recorder loop (b606558).
  • Power State Normalization: Safely normalized sysfs power supply statuses (Not charging, Full, etc.) into charging, discharging, or unknown (7f41e5c, 2ce8084).

⚡ Battery Accounting & Cycle Math

  • High-Precision Cycle Accumulator: Removed pre-accumulation rounding to ensure sub-milliwatt power draws accurately accumulate wear over time (86d1450).
  • Sub-Minute Cycle Deltas: Cycle estimations now compute against the immediate preceding 1-second debug sample instead of the 60-second historical window, capturing micro-bursts accurately (b0d1f5c).
  • Reboot Boundary Guard: Guarded cycle integration against reboots and time adjustments; drops in monotonic uptime or changes in boot_id reset reference samples rather than creating false cycle spikes (6266891).
  • Legacy DB Compatibility: Ensured legacy integer power states from existing database rows are cleanly mapped through derivePowerState (033f345).

🚨 Alert Engine & Diagnostics

  • Thermal Anomaly Detection: Added detection for thermal dissipation failure when CPU temps exceed 75°C while system load and CPU usage remain low (9bc21ed).
  • Debounce Hysteresis: Replaced immediate single-sample triggers with a 3-sample debounce latch (9a66e71).
  • Alert Latch Lifecycle: Automatically resets thermal anomaly latches when AC is reconnected, with contextual message formatting that reflects whether load average or CPU percentage qualified the alert (9d32635).
  • Charging Decoupling: Decoupled general CPU temperature alerts from AC charging states while gating low-battery warnings strictly to discharge mode (fcc3971, 2ce8084).

🗄️ Database, Storage & Migrations

  • Indexed Pruning Performance: Migrated the 1-second debug telemetry cleanup job from dynamic SQLite datetime('now', ...) calls to an indexed ISO timestamp string cutoff, eliminating full-table scans (7cf4e1b).
  • Strict Type Coercion: Ensured boolean columns stored as SQLite integers (0/1) are consistently deserialized to TypeScript booleans (cb2ca83).
  • Migration Table Conflict Handling: Hardened migration step 2–5 table renaming logic when the target table already exists (9f91cbb).
  • WAL Durability Documentation: Documented SQLite WAL commit boundaries, fsync frequency, and the 1000-page checkpoint threshold (5e67b90, 1e0548b).

🐧 Daemon, CLI & Installer

  • Accurate Oneshot CLI Sampling: batmon CLI now runs a double-sample pass in oneshot mode with a calibrated 500ms delay to report valid CPU deltas and process activity instead of zeros (323868e).
  • Daemon Tick Progression: Tick counters now advance unconditionally every second regardless of battery state (9742c1b).
  • Verified Btrfs Setup: The installer verifies chattr +C execution status before reporting success, warning the user if the filesystem prohibits attribute modifications (695804a).
  • Standalone Compilation: Added npm run build targeting a standalone binary via Bun (3925d9b).

📦 Upgrading

Existing SQLite databases will automatically migrate schemas on first start via SQLite user_version tracking. No manual database alterations are required.

If you are running the systemd daemon:

git pull
bun run install-service
systemctl --user restart batmon

v0.5.0

Choose a tag to compare

@InvictusNavarchus InvictusNavarchus released this 31 Aug 03:23
eac804b

v0.5.0

🎉 Highlights

This release introduces a stateful alert engine with hysteresis-based deadbands, replaces the lm-sensors dependency with a native sysfs hwmon parser (~7.2× faster, zero subprocess spawns), and adds dynamic battery discovery so the daemon works out-of-the-box on non-standard hardware (MacBook macsmc-battery, BAT1, auxiliary cells, etc.).


✨ Features

  • Stateful AlertManager with hysteresis & severity suppression (98ab6da)
    Replace pairwise edge-detection with a latch-based AlertManager class. Deadband thresholds eliminate notification storms for charge %, battery/CPU temperature, and battery health. Critical alerts now suppress lower-severity duplicates.

  • Auto-discover battery sysfs path (7706638)
    Scan /sys/class/power_supply at startup instead of hardcoding BAT0. Prioritises internal system batteries over peripherals (e.g. wireless mice). Supports BAT1, macsmc-battery, bat.0@aux:1, and other vendor naming schemes.

  • Native sysfs hwmon parser replaces lm-sensors (bb534de)
    Parse /sys/class/hwmon directly for AMD, Intel, ARM, and NVMe thermal/power sensors. Removes the sensors CLI dependency entirely. See empirical benchmark.


🐛 Bug Fixes

  • Alert tick interval corrected to 1 s (273aded)
  • High-charge alert no longer unlatches on Full status or brief AC toggles (fe30f6e) – re-arm now strictly requires discharge below the 75 % deadband.
  • UPower D-Bus object path matches daemon normalisation (29a75c6) – special characters (-, ., :, @) are sanitised to _, fixing resolution for non-standard battery names.
  • Guard against 0 / negative voltage_design_v (8d08454) – prevents permanent overvoltage false alarms on unsupported firmware. Overheat advice now differs between charging and discharging states.
  • GPU pairing preserved on multi-GPU / multi-AMDGPU systems (b379434, c107375) – temperature and power are always committed from the same device; deterministic hwmon enumeration order.
  • Accept 0 W GPU power (D3cold / runtime-PM sleep) and valid sub-zero temps down to −50 °C (5b810e0, 4d73568).

♻️ Refactoring & Cleanup

  • Extract formatUpowerDevicePath as a pure helper; keep upowerProp single-purpose (aea6d7e).
  • Remove legacy alert() wrapper and module-scoped defaultAlertManager singleton; standardise on AlertManager class (9027a31).
  • Remove redundant getLatestSample() DB query on daemon startup (3904130).
  • Integrate AlertManager into daemon lifecycle; decouple alert evaluation from historical SQLite rows; reset latches on battery removal (5df3bf4).
  • Extract named constants MIN_BAT_TEMP_TENTHS_C / MIN_HWMON_TEMP_MC with ACPI & hwmon unit-scaling docs (b83531e).

📝 Documentation

  • Document stateful hysteresis alert engine, deadband thresholds, and priority suppression in README (1c97a70).
  • Clarify overvoltage re-arm boundary ("at or below 10 % above design voltage") (3863c6a).
  • Update battery telemetry source labels to reflect dynamic discovery (555af7b).
  • Document UPower up_device_compute_object_path sanitisation algorithm (103029b).
  • Add empirical lm-sensors vs native hwmon evaluation with benchmark data (3c7b474).
  • Qualify hwmon driver-specific channel mappings and refine execution-latency claim (~5–8 ms/cycle) (0d10130, 4bd65b4).
  • Clarify discovery rationale and test-environment fallback in config (7830783).

🔧 Build & Install

  • Remove lm_sensors prerequisite check from install.sh (19243b3).
  • Remove unused release-assets block from .release-it.json (a68a3bf).

🧪 Tests

  • Comprehensive AlertManager suites: hysteresis bands, glitch debouncing, latch reset, priority suppression, and legacy-call persistence (98ab6da, 7526b04, fe30f6e).
  • UPower D-Bus path sanitisation for BAT0, macsmc-battery, bat.0@aux:1 (cd6c9ce, 29a75c6).
  • Multi-battery discovery and fallback handling (7706638).
  • Dual-AMDGPU and hybrid iGPU + dGPU pairing fixtures (c107375, b379434).
  • Replace spawnSync mocks with realistic sysfs directory-structure tests for AMD, Intel, ARM, NVMe, and edge-failure cases (381e8c1).

v0.4.0

Choose a tag to compare

@InvictusNavarchus InvictusNavarchus released this 28 Aug 02:43
5f92a24

v0.4.0

Telemetry: Native Linux Collectors Replace Glances

The external Glances HTTP dependency has been fully removed. All system metrics are now collected through direct Linux kernel interfaces with zero subprocess spawning:

  • CPU % — delta-based tick math via /proc/stat
  • Memory % — MemAvailable / MemTotal via /proc/meminfo
  • CPU Frequency — all-core scaling frequency averaging via sysfs cpufreq
  • GPU % — dynamic DRM sysfs device scan (gpu_busy_percent)
  • Top Processes — per-PID interval delta tracking via /proc/[pid]/stat (utime + stime), grouped by executable name with RSS memory

The process scanner now tracks tick-over-tick deltas instead of relying on ps %cpu, which reports a lifetime average. This correctly captures instantaneous CPU spikes for long-running processes during flight-recorder forensics. Telemetry latency drops from ~48 ms to ~13 ms per sample.

Deployment note: Glances is no longer required. Remove any Glances service or installation steps from your setup.

See docs/empirical-glances-vs-native-comparison.md for benchmark methodology and results.

Bug Fixes

  • Alert notification spam — non-charge alerts (battery temperature, health wear, charging over-voltage, CPU heat-soak) now evaluate previous state and only fire on threshold crossing, preventing repeated notifications every 60 s tick while conditions remain unchanged.
  • Database handle leak — setDbConnectionsForTesting now closes existing active connections before reassignment, preventing SQLite handle and lock leaks.

Internal / Developer Experience

  • Added unit test coverage for config, cycle estimation, migrations, alerts, DB operations, notify, and telemetry parsers.
  • Added scenario tests for daemon tick cadence and oneshot execution.
  • Introduced testability seams (injectable notifier, isolated DB handles, exported parser helpers, import.meta.main guard).
  • Refactored telemetry parsers with named destructuring, structured Map-based parsing, and named regex capture groups.
  • Removed unnecessary module exports.
  • Added knip (dead-code detection), lefthook (pre-commit formatting), and release-it (release workflow automation).
  • Updated README with testing/development instructions and revised dependency requirements.

Full diff: v0.3.0...v0.4.0