Skip to content

Hub Dashboard

github-actions[bot] edited this page Sep 18, 2026 · 2 revisions

πŸ“Š Bandwidth Hub Dashboard

Warning

Deprecated (v31.3+): The hub dashboard has been removed from the codebase. This document is retained for historical reference only. For fleet-wide visibility, use Prometheus metrics (urnet-tools metrics on) with the Monitoring bundle, or Grafana for custom dashboards.

Hub Dashboard Preview

A live fleet monitoring dashboard that aggregates bandwidth reports from all provider nodes. The hub runs as a standalone binary, accepts periodic POSTs to /api/report, and renders an HTML dashboard at the root path.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   POST /api/report (every 5-15m) β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Providerβ”‚  POST /api/heartbeat (every 15s)  β”‚  Hub    β”‚
β”‚  (node) β”‚ ────────────────────────────────▢ β”‚ :8080   β”‚
β”‚         β”‚                                    β”‚ :8443   β”‚
β”‚         β”‚  β”Œβ”€β”€ HTTPS (TLS with cert pin) ──┐ β”‚ (TLS)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                     β”‚
                                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                      β–Ό              β–Ό              β–Ό
                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                               β”‚  hub.db    β”‚ β”‚ In-memoryβ”‚ β”‚ GET /api/    β”‚
                               β”‚  (SQLite:  β”‚ β”‚ node map β”‚ β”‚ events (SSE) β”‚
                               β”‚   history) β”‚ β”‚ (no DB   β”‚ β”‚ β†’ dashboard  β”‚
                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  writes) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Each provider sends a bandwidth report containing per-proxy traffic counters, health status, and system metrics. The hub keeps the latest state from each node in an in-memory cache (used to render the dashboard and compute delta-based traffic rates between consecutive reports) and persists to a SQLite database, hub.db, in WAL mode.

Persistence is two-tier:

  • proxy_snapshots β€” one gzip-compressed snapshot of each node's full proxy list per report, retained 7 days. Compression shrinks a large proxy list by roughly an order of magnitude.
  • node_hourly β€” a small per-node, per-hour rollup (cumulative RX/TX, billable, peak clients, sample count), retained 365 days for long-range history.

On startup the hub rebuilds its in-memory cache from the latest stored snapshot of each node, so a restart doesn't blank the dashboard. A legacy hub.json from an older build is migrated into hub.db once on first boot, then retired to hub.json.imported.

In addition to the full report endpoint, providers can send lightweight heartbeats to /api/heartbeat every 15 seconds (configurable via HEARTBEAT_INTERVAL). Heartbeats carry Mbps rates, client/connection counts, contract stats, and memory β€” all updated in-memory only with zero DB writes. The dashboard re-fetches instantly when an SSE push arrives via GET /api/events.

Historical rollups are queryable at /api/history (params: node for a single node, hours for the lookback window; defaults to all nodes over 24h).

Quick Start

Option A: urnet-tools (native Linux installs, recommended)

If you installed via urnet-tools, the hub binary is bundled in the same release. One command installs and starts it as a systemd user service:

# Install hub as a systemd user service (downloads binary, enables on startup)
urnet-tools hub install

# Point the provider on this machine at the hub
urnet-tools hub set http://localhost:8080

# Point providers on other machines at this hub
urnet-tools hub set http://192.0.2.10:8080
# or with a domain name / HTTPS reverse proxy:
urnet-tools hub set https://hub.yourdomain.com

To stop hub reporting without uninstalling anything:

urnet-tools hub off

Stream the hub's own logs:

journalctl --user -fu urnetwork-hub.service

Note

hub install writes a systemd drop-in that persists across reboots. The hub database lives at ~/.local/share/urnetwork-hub/hub.db.

Warning

Port 8080 conflict with ENABLE_VNSTAT. The hub defaults to :8080 (bare-metal hub install hardcodes -addr :8080 with no port flag; Docker's hub install --docker and docker run default to -p 8080:8080). The provider's own ENABLE_VNSTAT=true traffic page (on by default in the Docker images) also binds container port 8080. If you colocate a provider and the hub on the same host, whichever starts second fails to bind and won't come up.

Fix β€” remap one of the two:

  • Docker hub: urnet-tools hub install --docker --port 8081 (or docker run -p 8081:8080 ...)
  • Docker provider: publish vnstat on a different host port, e.g. -p 8081:8080, or set ENABLE_VNSTAT=false if you don't need it on that node
  • Bare-metal hub: no install-time flag exists yet β€” use a systemd drop-in so the override survives a future hub install re-run (which regenerates the unit file):
    systemctl --user edit urnetwork-hub.service
    In the editor, add (matching the binary and data paths from the generated unit β€” check systemctl --user cat urnetwork-hub.service if yours differ):
    [Service]
    ExecStart=
    ExecStart=%h/.local/share/urnetwork-provider/bin/urnetwork-hub -addr :8081 -data %h/.local/share/urnetwork-hub
    Then systemctl --user daemon-reload && systemctl --user restart urnetwork-hub.service (and use :8081 in every hub set/hub link afterward)

Providers and the hub on separate hosts are unaffected β€” this only bites when they share a machine.

TLS Hub Setup (Built-in HTTPS)

The hub has built-in TLS with self-signed cert and trust-on-first-use pinning β€” no reverse proxy needed:

# Enable TLS on the hub (restarts it with HTTPS on :8443)
urnet-tools hub init

# Get the fingerprint (also printed by hub init) and pin it on a remote provider
urnet-tools hub test https://HUB_IP:8443    # verify TLS works

# From the provider machine, pin the hub's cert and point reports at it
urnet-tools hub link https://HUB_IP:8443

The hub auto-generates an ECDSA P-256 cert on first boot with TLS enabled. The provider stores the pinned SHA-256 fingerprint in ~/.urnetwork/hub.pin and verifies it on every connection. Mismatch = connection refused + debug info written to /tmp/hub-tls-debug.txt.


Option B: Docker (Windows / Mac / any host)

For hosts without systemd β€” Windows, macOS, or a Linux box where you'd rather not run a user service:

docker build -f hub/Dockerfile -t urnetwork-hub .
docker run -d --name urnetwork-hub -p 8080:8080 -v hubdata:/data urnetwork-hub

See Hub-Setup.md for the full walkthrough, including enabling TLS in-container.

Option C: Build & Run Manually

# Build from source
cd hub && go build -o hub .

# Run (default :8080, data in current dir)
./hub

# Custom port and data path
./hub -addr :9090 -data /var/hub-data

Configure Providers to Report

Set the URNETWORK_REPORT_URL environment variable on each provider at startup, or use the report command to set or change it at runtime.

Startup (Docker):

docker run -d \
  --name=urfix \
  -e URNETWORK_AUTH_CODE=YOUR_CODE \
  -e URNETWORK_REPORT_URL=http://HUB_IP:8080 \
  ghcr.io/full-bars/meso-miner:latest

Startup (native binary):

URNETWORK_REPORT_URL=http://HUB_IP:8080 ./ur-provider

Runtime (binary -- no restart):

# Set or change the report URL
urnet-tools report http://HUB_IP:8080

# Check current URL
urnet-tools report

# Disable reporting
urnet-tools report off

The report command writes to ~/.urnetwork/report_url, which the provider re-reads on every reporter tick (default 5m). No restart needed.

Runtime (Docker -- no restart):

The urnet-docker binary (v3.23.0-fix.27.0+; the legacy urnet-tools.ps1 wrapper has been retired) executes these commands inside the container via docker exec:

# Set or change
urnet-docker report http://HUB_IP:8080

# Check current
urnet-docker report

# Disable
urnet-docker report off

The Go binary handles the docker exec transparently -- it discovers the provider container and writes ~/.urnetwork/report_url inside it. The provider picks it up on the next tick. No container restart needed.

Tip

Use a single hub instance for your entire fleet. All nodes report to the same URL.

Secure Deployment

The hub supports two approaches for encrypted reporting:

Built-in TLS (No Reverse Proxy Needed)

The hub has a built-in TLS listener on :8443 with auto-generated self-signed ECDSA P-256 cert. Providers pin the cert fingerprint via urnet-tools hub link https://HUB_IP:8443. Best for deployments inside a trusted network (e.g., VPN between fleet nodes).

Reverse Proxy (For Public-Facing Hubs)

Tip

If your nodes are distributed across different networks, it is highly recommended to deploy the Hub behind a reverse proxy like Caddy. This provides automatic HTTPS (SSL/TLS) and allows you to use a clean Domain or DDNS URL.

1. Caddyfile Configuration Create or update your Caddyfile to route your domain to the local Hub instance (running on port 8080 by default):

hub.yourdomain.com {
    reverse_proxy localhost:8080
}

2. Node Configuration Configure your provider nodes to report to your new secure URL instead of the raw IP:

# Docker setup
-e URNETWORK_REPORT_URL=https://hub.yourdomain.com \

# Native binary setup
URNETWORK_REPORT_URL=https://hub.yourdomain.com ./ur-provider

Dashboard Features

Summary Cards

Five visual cards at the top of the dashboard:

  • Total Proxies β€” fleet-wide proxy count across all nodes
  • Healthy β€” proxies currently in "up" status with percentage of fleet
  • Degraded β€” proxies that were up but are now offline, plus dead count
  • Earning β€” percentage of up proxies currently generating billable traffic
  • Active Clients β€” total active sessions with aggregate RX/TX

Fleet Chart

A compact sparkline chart at the top of the Nodes tab shows aggregate RX/TX across the entire fleet for the last 24 hours. Loaded once on page load.

Filter Bar

Filter nodes by name (text search) and status (All / Healthy / Degraded / Dead) with real-time DOM filtering.

Node Table

Column Description
Node Node ID (name) with version, colored heartbeat dot (pulses when live)
Source IP Color-coded badge showing the node's source IP; same IP = same color, so same-NAT boxes cluster visually
TLS Green padlock icon if the node reported over HTTPS; blank for HTTP
Heartbeat Time since last report, with green/yellow/red health dot
Uptime Provider uptime
Proxies Color-coded status badges (up/degraded/dead)
Clients Active client sessions on this node
Earning Count of this node's proxies currently earning over up proxies
RX/TX/Bill RX/Bill TX Traffic volume with billable breakdown
In/Out Mbps Current bandwidth rate

Click any node row to open a slide-out drawer showing the full per-proxy detail list (ID, address, status, clients, age, traffic).

History Tab

Time series chart showing RX/TX over the last 24 hours, 3 days, or 7 days. Filter by individual node or view fleet aggregate. Reset zoom button restores the default range.

Performance

The hub dashboard is optimized to handle large fleets efficiently:

  • Gzip compression β€” all HTTP responses are gzip-compressed (HTML, JSON)

  • Lazy proxy loading β€” proxy details are fetched on demand via /api/nodes/<id>/proxies rather than embedded inline

  • JSON-only auto-refresh β€” the 30-second auto-refresh fetches lightweight node metadata via /api/nodes and updates the DOM in place; also triggered immediately by SSE push from GET /api/events when reports or heartbeats land

  • Rate limiting β€” per-IP rate limiter (60 req/min) protects against accidental or malicious request floods; /api/report is exempt so provider reports are never blocked

  • Stale node eviction β€” nodes that haven't reported in 15 minutes are automatically removed from the in-memory dashboard; they reappear when they report again

  • History query safety β€” history queries are capped at 7 days (168 hours) and 10,000 rows | | RX | Total received bytes / billable received (smaller text) | | TX | Total transmitted bytes / billable transmitted | | In Mbps | Inbound traffic rate (delta-based) | | Out Mbps | Outbound traffic rate (delta-based) | | Heap | Go heap memory (MiB) | | Conns | Active network connections |

  • Sortable: Click any column header to sort ascending/descending

  • Sort indicator: β–Ό (descending) or β–² (ascending) arrow in the active sort column

  • Default sort: By proxy count (highest first)

Per-Proxy Drilldown

Click any node row to expand its full proxy list:

  • ID β€” proxy identifier and address
  • Address β€” IP:port
  • Status β€” up / connecting / degraded / dead. A proxy stays connecting while its first WebSocket is still being established; if it never connects, the connecting state expires after one pulse cycle (~65 minutes) and a previously connected proxy falls back to a degraded tier, while a proxy that never connected at all reads as dead β€” so a hung respawn is distinguishable from a fresh one.
  • Earning β€” Yes/No badge; Yes when this proxy's billable bytes grew since the previous report and it currently has active clients (mirrors the provider's [traffic]/[profit] earning signal)
  • Clients β€” active sessions on this proxy
  • Max Age β€” longest-running connection
  • RX β€” total received bytes
  • TX β€” total transmitted bytes
  • Bill RX β€” billable received bytes
  • Bill TX β€” billable transmitted bytes

Click the expanded row again to collapse.

Live Updates via SSE

The dashboard subscribes to GET /api/events via the EventSource API. The hub pushes a data: refresh signal the instant a heartbeat or report lands, so the dashboard re-fetches node metadata immediately β€” no need to wait for the poll timer. A 30-second periodic poll stays active as a fallback for proxies or networks that buffer or strip SSE.

JSON API

For external tooling:

curl http://HUB_IP:8080/api/nodes             # full node state as JSON
curl -sN http://HUB_IP:8080/api/events        # SSE stream (data: refresh on updates)

/api/nodes returns the full node state as JSON, including all proxy details, system metrics, and a tls field indicating whether the last report arrived over HTTPS.

Heartbeats from providers are sent to /api/heartbeat (POST). The heartbeat carries the same node_id as the report and includes mbps_rx, mbps_tx, clients, contract counts, and per-proxy status changes β€” all updated in-memory without touching the database.

Report Format

Providers POST to /api/report with this structure:

{
  "node_id": "vegas",
  "host": "vegas.example.com",
  "version": "v3.23.0-fix.19",
  "uptime": 3600.0,
  "proxies": [
    {
      "id": "proxy[0] (192.168.1.1:1080)",
      "addr": "192.168.1.1:1080",
      "status": "up",
      "rx": 1024000,
      "tx": 512000,
      "bill_rx": 900000,
      "bill_tx": 400000,
      "clients": 3,
      "max_age_s": 120
    }
  ],
  "sys": {
    "heap_mib": 64,
    "sys_mib": 128,
    "conns": 42
  }
}

Rate Tracking

The hub computes per-node traffic rates as:

mbps = (current_bytes - previous_bytes) / elapsed_seconds Γ— 8 / 1_000_000

Rates are shown in the In Mbps and Out Mbps columns. A node needs at least two reports at least one second apart to show a rate.

Billable vs Total Traffic

Each proxy report carries both rx/tx (total wire bytes) and bill_rx/bill_tx (billable bytes). The dashboard distinguishes them:

  • Summary bar: RX 1.3 GB Β· 1.2 GB billable
  • Node rows: Total on top, billable below in muted text with b suffix
  • Proxy detail: Separate columns for Bill RX and Bill TX

This lets you see at a glance how much traffic is revenue-generating vs. total wire overhead.

Troubleshooting

Symptom Likely Cause
Node shows "β€”" for rates Only one report received; wait for the next report cycle
Node shows red dot No report received in >5 minutes
Dashboard shows stale data Provider process may be down or URNETWORK_REPORT_URL is misconfigured
TLS padlock shows for some nodes but not others Those nodes report to the HTTP port instead of HTTPS; run urnet-tools hub link https://HUB_IP:8443 on the provider
Hub TLS link fails with "fingerprint mismatch" The hub cert was regenerated (e.g., hub restart after cert file loss). Run hub test to inspect the new fingerprint, then hub link again to re-pin
No proxies in drilldown Provider sent empty proxy list; check provider logs
"b" shows 0 B for billable Proxy did not report billable bytes (may be in warmup state)

Data Storage

The hub persists to a SQLite database, hub.db, in the configured -data directory (WAL mode, so hub.db-wal and hub.db-shm sidecar files are normal). It can be inspected with any SQLite tool or backed up while the hub runs.

# Count reporting nodes
sqlite3 /var/hub-data/hub.db "SELECT COUNT(*) FROM nodes;"

# Latest per-node rollup totals
sqlite3 -header -column /var/hub-data/hub.db \
  "SELECT node_id, total_rx, total_tx, peak_clients FROM node_hourly ORDER BY hour DESC LIMIT 20;"

# Or fetch history over the API (last 7 days for one node)
curl -s 'http://localhost:8080/api/history?node=la6&hours=168' | jq .

Retention is automatic: proxy_snapshots are pruned after 7 days (hourly), node_hourly rollups after 365 days (daily).

Clone this wiki locally