bee-network-monitor
v1.0.0
Published
Monitor Bee node network activity: scrape Prometheus metrics (metrics) or count outbound TCP SYNs (syns)
Downloads
26
Readme
bee-network-monitor
Two monitoring modes for a Bee node, both writing timestamped CSV rows:
| Mode | What it measures |
|---|---|
| metrics | Application-layer counters scraped from the Bee Prometheus endpoint |
| syns | Outbound TCP SYN packets counted at the OS level via tcpdump |
Running both together gives a complete picture of application intent (metrics) vs. wire reality (syns), which is useful for diagnosing the port-scanning heuristic that hosting providers trigger on WSS-enabled nodes.
Requirements
- Node.js ≥ 18
metricsmode: network access to the Bee metrics endpoint (default:1633/metrics)synsmode: Linux,tcpdump, andCAP_NET_RAWor root
Grant CAP_NET_RAW without running as root
sudo setcap cap_net_raw,cap_net_admin+eip "$(which tcpdump)"Installation
npm install -g bee-network-monitorOr run without installing:
npx bee-network-monitor <mode> [args]Usage
bee-network-monitor metrics [metrics_url] [interval_sec] [output_csv]
bee-network-monitor syns [interface] [interval_sec] [output_csv]metrics mode
# All defaults: http://localhost:1633/metrics, every 15 s, bee_metrics.csv
bee-network-monitor metrics
# Custom endpoint, 30-second interval
bee-network-monitor metrics http://node-1:1633/metrics 30 node1_metrics.csvPolls the Prometheus endpoint every interval_sec seconds and appends one row. The CSV header is written once when the file does not yet exist.
Output columns
| Column | Type | Description |
|---|---|---|
| timestamp | string | UTC timestamp (ISO 8601) |
| bee_kademlia_total_outbound_connection_attempts | counter | Every dial kademlia has initiated — the primary port-scan signal |
| bee_kademlia_total_outbound_connection_failed_attempts | counter | Dials that returned a hard error |
| bee_kademlia_total_outbound_connections | counter | Successful outbound connections |
| bee_kademlia_currently_known_peers | gauge | Peer pool size driving dial activity |
| bee_kademlia_currently_connected_peers | gauge | Currently connected peer count |
| bee_kademlia_total_inbound_disconnections | counter | Inbound connections that have dropped; high churn forces re-dialing |
| bee_libp2p_created_connection_count | counter | Outbound connections opened by libp2p |
| bee_libp2p_handled_connection_count | counter | Inbound connections accepted |
| bee_libp2p_connect_breaker_count | counter | Circuit breaker trips (each = 100 consecutive failures to one peer) |
| bee_libp2p_blocklisted_peer_count | counter | Peers blocked for protocol violations |
| bee_libp2p_disconnect_count | counter | Locally initiated disconnections |
| bee_libp2p_stream_reset_count | counter | Stream resets (RST) |
| bee_libp2p_unexpected_protocol_request_count | counter | Requests for unsupported protocols |
| bee_handshake_syn_rx_failed | counter | Failed incoming handshake SYN reads |
| bee_handshake_ack_rx_failed | counter | Connections opened but ACK never completed |
| bee_reacher_ping_attempt_count | counter | Pings sent to currently-connected peers |
| bee_reacher_ping_error_count | counter | Failed reacher pings |
| bee_reacher_peers | gauge | Reacher queue depth (should equal connected peers) |
| bee_process_network_receive_bytes_total | counter | Cumulative bytes received |
| bee_process_network_transmit_bytes_total | counter | Cumulative bytes sent |
| bee_process_open_fds | gauge | Open file descriptors (each TCP socket = 1 FD) |
Derived rates (compute from successive rows)
kademlia_dial_rate = Δ outbound_connection_attempts / Δt
kademlia_dial_success_ratio = Δ outbound_connections / Δ outbound_connection_attempts
reacher_ping_failure_ratio = Δ ping_error_count / Δ ping_attempt_count
rx_bytes_per_sec = Δ network_receive_bytes_total / Δt
tx_bytes_per_sec = Δ network_transmit_bytes_total / Δtsyns mode
# All defaults: eth0, every 10 s, syn_bursts.csv
sudo bee-network-monitor syns
# Specific interface and interval
sudo bee-network-monitor syns ens3 10 node1_syns.csvSpawns tcpdump each interval with a BPF filter that passes only outbound TCP SYN (SYN=1, ACK=0) packets sourced from the interface's IPv4 address. Each packet line is parsed and discarded immediately; only counters and two Sets (unique IPs, unique ports) are kept in memory.
Output columns
| Column | Description |
|---|---|
| timestamp | UTC timestamp (ISO 8601) |
| interval_sec | Capture window length in seconds |
| syn_out_count | Total outbound SYN packets in the interval |
| unique_dst_ips | Distinct destination IP addresses reached |
| unique_dst_ports | Distinct destination ports reached |
| syns_per_sec | syn_out_count / interval_sec |
| unique_ips_per_sec | unique_dst_ips / interval_sec — closest analogue to what hosting providers observe |
| kernel_drops | Packets dropped by the kernel ring buffer; non-zero means counts are understated |
Interpreting the output
unique_ips_per_sec is the metric to watch. Hosting providers do not publish detection thresholds, so compare against your own baseline:
- Record a baseline with WSS disabled.
- Enable WSS, record again.
- The delta is concrete evidence of exactly how much additional connection spread WSS introduces.
A brief spike after node startup or a wave of peer disconnections is expected — kademlia refilling its bins. A value that never returns to baseline is worth investigating.
If kernel_drops is non-zero, increase the ring buffer size:
sudo bee-network-monitor syns eth0 10 out.csv # default ring buffer
sudo tcpdump -B 4096 ... # increase to 4 MB if drops persistCorrelating both modes
Both CSVs include a UTC timestamp column but are written by independent processes, so timestamps will not align exactly. Round to the nearest interval boundary to correlate them:
kademlia_dial_rate (from metrics) ≈ unique_ips_per_sec (from syns)If syns reads significantly higher than metrics implies, the excess comes from outside Bee — AutoTLS certificate renewal, DNS resolution for peer addresses, or other system processes.
Development
npm install
npm start -- metrics # run from TypeScript source
npm start -- syns eth0
npm run typecheck
npm run build # emits dist/index.js