Skip to content

Observability

Overview

ifm-dashboard is an observability/telemetry consumption layer only. It does not participate in the IFM runtime data path (audio, transport, relay, or discovery). Nodes continue operating normally if the dashboard is unavailable.

There are two telemetry channels, both produced by ifm-core (the protocol's single source of truth):

  1. Mesh telemetry plane (primary — the dashboard's real source). Every node with NodeConfig.telemetry enabled (default) publishes its JSON observability snapshot on the reserved control topic /ifm/telemetry/v1 every 5 seconds. Frames ride the gossip control plane exactly like station records / relay announcements — no polling, no inbound connections — so ANY node that is in the mesh sees every other node's live state. The dashboard is such a node: it joins the real network through the rendezvous (like every app) and reads the frames.
  2. Local observability server (optional, for same-machine debugging). A node can additionally serve read-only JSON snapshots on 127.0.0.1:<port> (NodeConfig.observability_port).

Responsibilities

  • Discover/track all running IFM nodes — via the observability interface; node listings are read-only snapshots
  • Show node status and health — CPU, memory, network, gossip, DHT, audio pipeline
  • Observe node-level logs — streamed log lines from each connected node
  • Aggregate application/system logs — where nodes expose structured log output
  • Monitor connections and peers — per-peer connection state, RTT, transport mode
  • Monitor relay activity — pool membership, forwarded packets, uptime (read-only)
  • Monitor audio/stream state and metrics — opus encode/decode health, jitter buffer stats, VAD state, microphone level
  • Provide historical observability — retained packet logs, frequency uptime, connection history (configurable retention)
  • Provide debugging/diagnostic information — packet captures, topology snapshots, node introspection

Architectural Constraints

ConstraintDetail
No runtime dependencyDashboard may be taken offline; IFM nodes continue operating
Observes onlyNever broadcasts, tunes, announces, or sends control commands — all interactions are read-only
Observer nodeThe dashboard runs a real ifm-core node so it can see the REMOTE mesh: it joins through the rendezvous like every app. Being in the mesh is what lets it observe it; it never produces traffic (its own telemetry frame is the only thing it sends, like every node's)
Telemetry planeTelemetry rides the reserved control topic (/ifm/telemetry/v1), the same class of tiny control-plane traffic as station records / relay announcements. Frames are advisory observer data — nothing in the protocol ever acts on them
Telemetry interfaceConsumes the telemetry frames + discovery records + its own transport view, all produced by ifm-core (the protocol's single source of truth). The dashboard is a Rust/Tauri application; the SDK is not involved
Passive forwardingDashboard never becomes a dependency of the IFM runtime data path
Transport agnosticObservability data rides the mesh control plane (any transport), plus the optional localhost HTTP channel for same-machine debugging

Telemetry Interface

ifm-core's Node exposes an observability server on 127.0.0.1:<port> (NodeConfig.observability_port, disabled by default). Each request builds a read-only snapshot under short-lived locks; the server runs on its own thread with timeouts, so a slow or missing dashboard never stalls the node.

TopicEndpointDescription
node.infoGET /infoNode identity, version, transport, listen addresses, uptime
node.healthGET /healthGossip counters, DHT, peers, audio pipeline metrics
node.peersGET /peersConnected peers with addresses + transport
node.relayGET /relayRelay pool status, serving state, forwarded packet count
node.audioGET /audioOpus health, jitter buffer stats, VAD state, live-edge
node.p2pGET /p2pP2P health: latency, jitter, loss, upload, role, active path
node.frequenciesGET /frequenciesTuned frequencies + on-air station channels, listener count
node.logsGET /logsReserved — structured log streaming is a later phase (returns { lines: [] } today)
GET /snapshotEverything above in one JSON document (dashboard poll)

Mesh Telemetry Plane

  • Transport (crates/transport): the reserved gossip topic /ifm/telemetry/v1 carries [TelemetryFrame]s — { node_id, ts_ms, data } where data is the node's opaque JSON snapshot. Every transport (libp2p QUIC, in-memory, shared) auto-subscribes at startup, keeps the freshest frame per node (TTL-bounded — a silent node drops out on its own), and exposes publish_telemetry / telemetry_frames.
  • Core (crates/core/src/telemetry.rs): Node::publish_telemetry() serializes the node's observability_snapshot() and publishes it; a background thread does this every TELEMETRY_INTERVAL (5s) when NodeConfig.telemetry is on (default). Node::telemetry() returns the freshest decoded frame per node (MeshTelemetry).
  • Dashboard: mesh_telemetry (Tauri command) exposes Node::telemetry() to the WebView.

Implementation

Node side — crates/core/src/observability.rs

  • NodeSnapshot — serde types covering every topic above (info, health, peers, relay, p2p, paths, audio, frequencies).
  • The p2p block + paths list come from the Path Manager (crates/core/src/path.rs, spec §19a): every candidate path — direct peers and pool relays — is scored from measured RTT, jitter, loss, bandwidth, hops and stability, best-first. The dashboard shows the current P2P health (latency/jitter/loss/upload/role/path) and the ranked alternatives, so an operator sees why the node is on its current path and which relay could beat it.
  • ObservabilityServer — a tiny std-only HTTP server (no new dependencies) bound to 127.0.0.1:<port>. One request per connection, read timeout, JSON responses. Holds a Weak<Node> and exits when the node is dropped.
  • Wired in Node::build: when NodeConfig.observability_port is Some, the server starts on that port. Desktop apps pick per-app ports (14320 + AppKind), so the dashboard can scan a known range.

Dashboard side — packages/ifm-dashboard

A Tauri 2 desktop application whose Rust backend runs a real ifm-core observer node (AppKind::Dashboard via ifm-desktop):

  1. node_start boots the observer node with the default REMOTE entry — it fetches the rendezvous manifest (bootstrap.ifm.json), dials the entry, and discovers the mesh (DHT + control topics), exactly like every app.
  2. mesh_telemetry returns the freshest telemetry frame per node heard on the mesh (its own included). station_list / relay_list return the signed discovery records. node_info / node_stats / node_peers expose the observer's own transport view.
  3. The React frontend polls those commands every ~2.5s and renders live panels: node cards with per-node P2P health (latency / jitter / loss / upload / role / path), stations, relays, topology graph (React Flow), packet feed.

Deployment

ifm-dashboard is a standalone desktop application that:

  • Joins the real IFM network through the rendezvous as an observer node (LAN-only under IFM_LAN=1, same as every app)
  • Never broadcasts, tunes, or announces — it cannot affect the network
  • Is updated independently of IFM node releases (backward-compatible interface)

It is internal tooling: it is built and run by IFM developers and operators during development and operations. It is never shipped to end users.

Released under the MIT License.