mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
fipstop: ratatui-based TUI for real-time monitoring of a running FIPS daemon. Tabs and navigation: - 8 navigable tabs: Node, Peers, Transports, Sessions, Tree, Filters, Performance, Routing - Tab/BackTab navigation with group separators in tab bar - Table views with selectable rows, detail drill-down panels, and scrollbars Node tab: - Runtime info: pid, exe path, uptime, control socket path, TUN adapter name - Identity: npub, node_addr, ipv6 address - State summary with peer/session/link/transport/connection counts - TUN IPv6 traffic and forwarded transit traffic counters Peers tab: - Table with Name, Address, Conn, Depth, SRTT, Loss, LQI, Pkts Tx/Rx - Detail panel: identity, connection info, transport cross-reference, tree/bloom state, link stats, MMP metrics with LQI Sessions tab: - Table with Name, Remote Addr, State, Role, SRTT, Loss, SQI, Path MTU, Last Activity - Detail panel: identity, session info, traffic stats, MMP metrics with SQI Transports tab: - Hierarchical tree view: expandable transport parents with nested links (▼/▶ indicators, ├─/└─ tree chars, Space/Arrow to expand/collapse) - Transport detail: type-specific stats (UDP/TCP/Ethernet) - Link detail: peer cross-reference with MMP metrics and LQI Performance tab: - Link-layer MMP: SRTT, loss, ETX, LQI, goodput per peer - Session-layer MMP: SRTT, loss, ETX, SQI, path MTU per session - Trend indicators (rising/falling/stable) with context-aware coloring Routing tab: - Routing state: cache sizes, pending lookups, recent requests - Coordinate cache: entries, fill ratio, TTL, expiry, avg age - Statistics: forwarding, discovery request/response, error signal counters Tree tab: - Spanning tree position with 16 announce stats (inbound/outbound/cumulative) Filters tab: - Bloom filter announce stats, per-peer fill ratio and estimated node count MMP metrics enhancements: - Add etx_trend DualEwma for smoothed ETX tracking - Add smoothed_loss() and smoothed_etx() accessors (long-term EWMA) - LQI (Link Quality Index) = smoothed_etx * (1 + srtt_ms / 100) - SQI (Session Quality Index) = same formula for session layer - All loss/ETX displays prefer smoothed values with raw fallback Control socket: - Add smoothed_loss, smoothed_etx, lqi/sqi to show_peers, show_sessions, and show_mmp JSON responses - Rename fips_address to ipv6_addr in show_status and show_peers - Add tun_name and control_socket to show_status - FHS-compliant 3-tier default path: $XDG_RUNTIME_DIR, /run/fips, /tmp Node extensions: - Add started_at/uptime() to Node - Add tun_name() accessor Docker sidecar updates: - TCP transport support via FIPS_PEER_TRANSPORT env var - Build scripts include fipstop binary
66 lines
2.0 KiB
Rust
66 lines
2.0 KiB
Rust
use serde_json::Value;
|
|
use std::path::{Path, PathBuf};
|
|
use std::time::Duration;
|
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
|
use tokio::net::UnixStream;
|
|
use tokio::time::timeout;
|
|
|
|
const IO_TIMEOUT: Duration = Duration::from_secs(5);
|
|
|
|
pub struct ControlClient {
|
|
socket_path: PathBuf,
|
|
}
|
|
|
|
impl ControlClient {
|
|
pub fn new(socket_path: &Path) -> Self {
|
|
Self {
|
|
socket_path: socket_path.to_path_buf(),
|
|
}
|
|
}
|
|
|
|
pub async fn query(&self, command: &str) -> Result<Value, String> {
|
|
let stream = timeout(IO_TIMEOUT, UnixStream::connect(&self.socket_path))
|
|
.await
|
|
.map_err(|_| "connection timed out".to_string())?
|
|
.map_err(|e| format!("connect: {e}"))?;
|
|
|
|
let (reader, mut writer) = stream.into_split();
|
|
|
|
let request = format!("{{\"command\":\"{command}\"}}\n");
|
|
timeout(IO_TIMEOUT, writer.write_all(request.as_bytes()))
|
|
.await
|
|
.map_err(|_| "write timed out".to_string())?
|
|
.map_err(|e| format!("write: {e}"))?;
|
|
|
|
writer
|
|
.shutdown()
|
|
.await
|
|
.map_err(|e| format!("shutdown: {e}"))?;
|
|
|
|
let mut buf_reader = BufReader::new(reader);
|
|
let mut line = String::new();
|
|
timeout(IO_TIMEOUT, buf_reader.read_line(&mut line))
|
|
.await
|
|
.map_err(|_| "read timed out".to_string())?
|
|
.map_err(|e| format!("read: {e}"))?;
|
|
|
|
let response: Value =
|
|
serde_json::from_str(line.trim()).map_err(|e| format!("parse: {e}"))?;
|
|
|
|
let status = response
|
|
.get("status")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("unknown");
|
|
|
|
if status == "error" {
|
|
let msg = response
|
|
.get("message")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("unknown error");
|
|
return Err(msg.to_string());
|
|
}
|
|
|
|
Ok(response.get("data").cloned().unwrap_or(Value::Null))
|
|
}
|
|
}
|