Skip to content

feat: add network probes (ICMP/TCP/HTTP) with latency and packet loss monitoring#1911

Open
XiaoMiku01 wants to merge 21 commits intohenrygd:mainfrom
XiaoMiku01:feat/network-probes
Open

feat: add network probes (ICMP/TCP/HTTP) with latency and packet loss monitoring#1911
XiaoMiku01 wants to merge 21 commits intohenrygd:mainfrom
XiaoMiku01:feat/network-probes

Conversation

@XiaoMiku01
Copy link
Copy Markdown

📃 Description

Add network probes (ICMP/TCP/HTTP) to Beszel, allowing users to monitor latency and packet loss from any monitored agent to arbitrary targets. Probes are configured per-system via the UI, executed on the agent side with results aggregated over a 60-second sliding window, and displayed as a table + latency chart on the system detail page. Realtime mode is also supported.

📖 Documentation

N/A

🪵 Changelog

➕ Added

  • New SyncNetworkProbes and GetNetworkProbeResults WebSocket action types for hub-agent communication
  • Shared probe entity types (Config, Result) in internal/entities/probe/
  • Agent-side ProbeManager with ICMP (unprivileged ping), TCP, and HTTP probe execution on configurable intervals
  • Agent WebSocket handlers for syncing probe configs and returning aggregated results
  • Hub-side network probe REST API (/api/beszel/network-probes, /api/beszel/network-probe-stats) for CRUD and stats retrieval
  • Hub-side probe sync, result collection, and 1-minute stats aggregation logic
  • network_probes and network_probe_stats PocketBase collections (initial snapshot + upgrade migration)
  • Probe results included in the 1-second realtime WebSocket broadcast
  • Frontend NetworkProbeRecord and NetworkProbeStatsRecord TypeScript types
  • Probe creation dialog with protocol selection (ICMP/TCP/HTTP), target, port, interval, and name fields
  • Network probes table displaying protocol badges, latency, packet loss, and delete actions
  • Latency line chart using ChartCard with historical stats data
  • Lazy-loaded NetworkProbes component integrated into both default and tabbed system detail layouts

✏️ Changed

  • use-system-data.ts updated to pass system record as a dependency for probe stats auto-refresh

🔧 Fixed

  • Use shared http.Client in ProbeManager to avoid connection/transport leaks
  • Skip probe goroutine and agent request when system has no enabled probes
  • Validate HTTP probe target URL scheme (must be http:// or https://) on creation
  • Filter deleted probes from latency chart stats to avoid stale data
  • Rename setInterval to setProbeInterval to avoid shadowing the global
  • Move probeKey function outside component (pure function, no closure needed)
  • Fix probes.length dependency to use probes directly in effect deps
  • Use proper TypeScript type for stats fetch instead of any
  • Fix name column fallback to show target instead of dash

🗑️ Removed

  • N/A

📷 Screenshots

image

XiaoMiku01 and others added 18 commits April 11, 2026 01:21
Implements the core probe execution engine (ProbeManager) that runs
network probes on configurable intervals, collects latency samples,
and aggregates results over a 60s sliding window. Adds two new
WebSocket handlers (SyncNetworkProbes, GetNetworkProbeResults) for
hub-agent communication and integrates probe lifecycle into the agent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add TypeScript interfaces for the network probes feature API responses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dialog component for adding ICMP/TCP/HTTP network probes with
protocol selection, target, port, interval, and name fields.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Displays probe list with protocol badges, latency/loss stats, and
delete functionality. Includes a latency line chart using ChartCard
with data sourced from the network-probe-stats API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lazy-load the NetworkProbes component in both default and tabbed
layouts so the probes table and latency chart appear on the system
detail page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename setInterval to setProbeInterval to avoid shadowing global
- Move probeKey function outside component (pure function)
- Fix probes.length dependency to use probes directly
- Use proper type for stats fetch instead of any
- Fix name column fallback to show target instead of dash

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stats records in the DB contain historical data for all probes including
deleted ones. Now filters stats by active probe keys and clears state
when all probes are removed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Include probe results in the 1-second realtime WebSocket broadcast so
the frontend can update probe latency/loss every second, matching the
behavior of system and container metrics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Existing databases from main branch lack the network_probes and
network_probe_stats collections, which were only in the initial snapshot.
This separate migration ensures they are created on upgrade.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use shared http.Client in ProbeManager to avoid connection/transport leak
- Skip probe goroutine and agent request when system has no enabled probes
- Validate HTTP probe target URL scheme (http:// or https://) on creation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass system record to NetworkProbes component and use it as a
dependency in the non-realtime fetch effect, matching the pattern
used by system_stats and container_stats in use-system-data.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Apply appendData() for gap detection in both realtime and non-realtime
  modes, so the latency chart shows breaks instead of smooth lines when
  data is missing during service interruptions
- Handle null stats in gap marker entries to prevent runtime crashes
- Fix color assignment: use CSS variables (--chart-1..5) for ≤5 probes,
  switch to dynamic HSL distribution for >5 probes so all lines are
  visible with distinct colors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the ping-command-only implementation with a three-tier
approach using golang.org/x/net/icmp:

1. Raw socket (ip4:icmp) — works with root or CAP_NET_RAW
2. Unprivileged datagram socket (udp4) — works on Linux/macOS
   without special privileges
3. System ping command — fallback when neither socket works

The method is auto-detected on first probe and cached for all
subsequent calls, avoiding repeated failed attempts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve the target hostname before starting the timer so the
measurement reflects pure TCP handshake time only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@XiaoMiku01 XiaoMiku01 requested a review from henrygd as a code owner April 11, 2026 13:54
@henrygd
Copy link
Copy Markdown
Owner

henrygd commented Apr 11, 2026

Thank you for your work on this. I've wanted to add this feature but haven't had the time. The implementation seems very similar to what I envisioned. I can't review immediately but will get back to you soon.

XiaoMiku01 and others added 3 commits April 17, 2026 13:49
Resolved conflict in internal/records/records.go:
- Upstream refactor moved deletion code to records_deletion.go and
  switched averaging functions from package-level globals to local
  variables (var row StatsRecord / params := make(dbx.Params, 1)).
- Kept AverageProbeStats and rewrote it to match the new local-variable
  pattern.
- Dropped duplicated deletion helpers from records.go (they now live in
  records_deletion.go).
- Added "network_probe_stats" to the collections list in
  records_deletion.go:deleteOldSystemStats so probe stats keep the same
  retention policy.
@henrygd henrygd moved this to In Progress in Beszel Roadmap Apr 20, 2026
@henrygd
Copy link
Copy Markdown
Owner

henrygd commented Apr 20, 2026

Working on this now. No need to merge any updates to main going forward 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants