4.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
RMon is a real-time data monitor and plotter for distributed control systems accessed over SSH. The UI runs locally; data is acquired by a remote agent. See usecase.md for requirements and docs/ for full specs.
Language and Tech Stack
Rust throughout — single language for agent, UI, and shared types.
| Crate | Role | Key deps |
|---|---|---|
rmon-common |
Shared types, protocol message enums | serde, bincode |
rmon-agent |
Remote data acquisition server | tokio, tracing, anyhow, toml, serde |
rmon-ui |
Operator desktop application | egui, eframe, egui_plot, tokio, tracing |
Build Commands
# Build everything (debug)
cargo build
# Build the agent as a fully static binary (primary deployment target)
cargo build --release --target x86_64-unknown-linux-musl -p rmon-agent
# Build agent for ARM (e.g. remote Raspberry Pi)
cargo build --release --target aarch64-unknown-linux-musl -p rmon-agent
# Build the UI for the current platform
cargo build --release -p rmon-ui
# Run tests
cargo test
# Run tests for a single crate
cargo test -p rmon-common
cargo test -p rmon-agent
# Run a single test
cargo test -p rmon-agent sources::csv::tests::parse_timestamp
# Lint
cargo clippy --all-targets --all-features
# Format
cargo fmt
Architecture (short version)
Two executables communicate via a binary TCP protocol over an SSH tunnel:
-
rmon-agent: runs on the remote machine, acquires data from heterogeneous sources (CSV, UDP, EPICS…), stores it, streams it to connected clients. Built as a fully static musl binary — single file, zero runtime deps,scp+ run. -
rmon-ui: runs on the operator's local desktop. Manages the SSH tunnel automatically (uses the systemsshbinary + user's~/.ssh/config). Renders real-time plots withegui/egui_plot. -
rmon-common: library crate shared by both. Contains all types (SignalId,SignalInfo,Sample,StorageMode) and theClientMessage/AgentMessageprotocol enums serialized withbincode.
Full architecture: docs/architecture.md
Wire protocol: docs/protocol.md
Data model: docs/data-model.md
Agent config and source trait: docs/agent-spec.md
UI layout and state machine: docs/ui-spec.md
Key Design Decisions
- Protocol framing:
[u32 LE length][bincode payload]— no gRPC/HTTP overhead. Both sides are Rust sobincode+ shared enum types is simplest. - Agent portability:
x86_64-unknown-linux-muslgives a fully static binary requiring only Linux kernel ≥ 3.2. The agent never has a GUI, so no X11/GL needed. - Remote access UX: The UI deploys the agent binary via SSH itself (like VSCode Remote). Users only need to enter their SSH host alias; existing
~/.ssh/config(includingProxyJump) handles multi-hop routing. - UI rendering:
eguiimmediate-mode withglow(OpenGL 2.1) backend. For signals with more points than screen pixels, min-max downsampling runs on the CPU before drawing. - Timestamps: all source timestamps are normalized to
i64nanoseconds since Unix epoch inside the source adapter. The rest of the codebase only seesi64. - Concurrency (agent): one tokio task per data source →
broadcastchannel → dispatcher fans out to storage writers and per-client session tasks. - UI/async boundary: tokio runtime in a background thread; egui on main thread.
std::sync::mpscchannels carryUiEvent(agent→UI) andCommand(UI→agent).
Adding a New Data Source
- Create
rmon-agent/src/sources/{name}.rs - Implement the
DataSourcetrait (name(),signals(),async fn run(self, tx)) - Add a new variant to
SourceConfiginconfig.rsand construct it insources/mod.rs - Add config example to
docs/agent-spec.md
Protocol Changes
All protocol types live in rmon-common/src/protocol.rs. Since bincode is not self-describing, any change to ClientMessage or AgentMessage requires a protocol version bump in the Handshake message and both sides must be rebuilt together.