diff --git a/.gitignore b/.gitignore index bf2d14e..24de080 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,6 @@ build/ *.exe # Local editor tooling -.cursor/skills/ -.agents/ -skills-lock.json .vscode/ .idea/ *.swp diff --git a/README.md b/README.md index 393902e..8dcd13c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The default command opens the dashboard. See [usage and project details](docs/re ## Architecture -The CLI starts the interactive dashboard by default, or runs the HTTP service, exports readings, or generates shell completions. The monitor collects system and process readings into a shared snapshot. The dashboard displays that snapshot, while remote dashboard and export commands fetch it from an agent. The agent serves health and metrics endpoints over HTTP and only binds to a loopback address. Export writes snapshots as JSON and CSV. +The CLI starts the interactive dashboard by default, or runs the HTTP service, exports readings, or generates shell completions. The monitor collects system and process readings into a shared snapshot. The dashboard displays that snapshot, while the `--remote ` option of the dashboard and export commands fetches the snapshot from a running agent instead. The agent serves health and metrics endpoints over HTTP and only binds to a loopback address, so reaching it from another machine needs a tunnel or authenticated proxy that grainx does not provide. Export writes snapshots as JSON and CSV. ## Tests diff --git a/docs/readme-details.md b/docs/readme-details.md index cb2a40d..d658543 100644 --- a/docs/readme-details.md +++ b/docs/readme-details.md @@ -15,21 +15,6 @@ The term *agent* in this repository refers to the HTTP metrics daemon process, n - To build an optimized binary, run `cargo build --locked --release`. - In a headless environment, use the agent or export command instead. -## Architecture details - -- **Monitor (`SystemMonitor`)**: Gathers raw state using `sysinfo`. -- **Analytics Engine (`analytics::*`)**: Performs formula evaluation and anomaly detection on the collected metrics. `evaluate_metric_formula` substitutes whitespace-separated tokens and evaluates strictly left-to-right with no operator precedence; see its doc comment for the full contract. -- **Agent Server (`agent::*`)**: Exposes collected metrics via an HTTP server using `axum`. Binds to `127.0.0.1:9090` by default and refuses any non-loopback address, because it serves without TLS or authentication. -- **TUI Renderer (`ui::*`, `tui::*`)**: Renders the terminal dashboard using `crossterm`. Employs frame-skipping and adaptive refresh intervals to gracefully degrade under heavy load. - -## Limitations details - -- **Security**: The HTTP agent has no TLS, authentication, or rate limiting. It refuses any non-loopback bind address at runtime, and `src/agent.rs` tests that `0.0.0.0`, `::`, and ordinary interface addresses are rejected. Loopback does not isolate users or processes on the same host; remote metrics access requires placing an authenticated transport in front of the agent. -- **Completeness**: Network and disk I/O are aggregates and do not drill down into per-socket or per-file statistics. -- **Formula Evaluator**: `evaluate_metric_formula` evaluates whitespace-separated tokens strictly left-to-right with no operator precedence or parentheses ([src/analytics.rs](../src/analytics.rs)). -- **Interactive TUI**: The `monitor` command requires an interactive terminal (TTY) and exits with an error in headless environments ([src/tui.rs](../src/tui.rs)). -- **OS Support**: CI tests Linux and macOS on stable Rust. Windows is not verified and is not covered by the CI matrix. - ## Commands ~~~bash @@ -56,7 +41,7 @@ cargo run --locked -- monitor --remote http://127.0.0.1:9090 cargo run --locked -- completions bash ~~~ -The agent exposes host metrics without authentication, TLS, or rate limiting, so it refuses to start on any address that is not loopback: `--bind 0.0.0.0` and a LAN address exit with an error instead of listening. That boundary is enforced in the program, not only in this document. Loopback is still not a complete protection — it does not separate users or processes on the same machine, and anything that can reach localhost can read the metrics. Remote metrics access is out of scope for this version; if you need it, put an authenticated transport in front of the agent. +The agent exposes host metrics without authentication, TLS, or rate limiting, so it refuses to start on any address that is not loopback: `--bind 0.0.0.0` and a LAN address exit with an error instead of listening. That boundary is enforced in the program, not only in this document. Loopback is still not a complete protection — it does not separate users or processes on the same machine, and anything that can reach localhost can read the metrics. Remote monitor mode (`monitor --remote `) and remote export (`export --remote `) fetch `GET /metrics` from a running agent by URL, so they work against an agent on the same host or one reached through a tunnel or authenticated proxy that you provide. grainx itself does not expose the agent to a network and adds no authentication or TLS. In remote monitor mode the dashboard shows the agent's snapshot; process termination does not act on the remote host. ## Configuration diff --git a/src/input.rs b/src/input.rs index 69d8c11..cdcf62f 100644 --- a/src/input.rs +++ b/src/input.rs @@ -53,7 +53,7 @@ pub fn handle_input( if backend.kill_process(*pid) { canvas.set_cursor(0, layout.proc_start_y + 11)?; canvas.set_color(palette.ok)?; - canvas.draw_str(&format!("Process {name} killed successfully!"))?; + canvas.draw_str(&format!("Process {name} killed."))?; } else { canvas.set_cursor(0, layout.proc_start_y + 11)?; canvas.set_color(palette.critical)?; @@ -102,9 +102,14 @@ pub fn handle_input( KeyCode::Char('a') => { if let Some(perf) = perf_monitor { perf.toggle_adaptive_refresh(); + let state = if perf.get_performance_stats().2 { + "on" + } else { + "off" + }; canvas.set_cursor(0, 0)?; canvas.set_color(palette.header)?; - canvas.draw_str("Adaptive refresh toggled!")?; + canvas.draw_str(&format!("Adaptive refresh {state}."))?; } } _ => {} diff --git a/src/monitor.rs b/src/monitor.rs index a6e23ab..5d7f3bb 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -151,7 +151,7 @@ impl SystemMonitor { self.high_cpu_duration += 1; if self.high_cpu_duration == 5 { self.push_alert( - "WARNING: Sustained high CPU usage detected! Potential CPU bottleneck.", + "WARNING: Sustained high CPU usage detected. Potential CPU bottleneck.", ); } } else {