NetPulse is a read-only PWA for monitoring a home network built on OpenWrt/GL.iNet routers: fleet status, per-router health, connected devices, a live topology map, WireGuard peers, AdGuard Home stats and alerts, in real time. One static Go binary with the frontend embedded, self-hosted on a small Linux box.
I believe in digital sovereignty: if a device makes you depend on its cloud, its firmware or its vendor, it isn't 100% yours. That's why I've always favored hardware I can flash or root. My home layout ended up with four routers: a Flint 2 as the main one and three Xiaomi AX6 access points bought second-hand for 30 euros each. Cheap, powerful, and all running OpenWrt. That sovereignty let me orchestrate and personalize the network exactly how I wanted (not without some challenges), but I always missed a unified view of what was going on: what connects where, what's healthy, what isn't. There was nothing out there, or I couldn't find it, so I built it. NetPulse is that global viewer: it analyzes your network, spots anomalies, and warns you.
- Go, single static binary: a 24/7 monitor on a small LXC. The stdlib
net/httpServeMux, no framework,go:embedfor the frontend. Upgrade is swapping one file. modernc.org/sqlite, CGO off: fully static, no C toolchain needed on the target. Time series, users and sessions in one embedded SQLite file (WAL).- Read-only by design: the server generates its own ed25519 keypair;
you authorize the public key on each router and it only ever reads
(ubus,
/proc, iwinfo,bridge fdb,wg show). It cannot change your network. - React 19 + Vite + Tailwind PWA: installable, live over SSE (5 s), the same UI shell as my other apps.
- systemd, no Docker: it monitors a network; it doesn't need a container to do it.
- Fleet overview: health score, live traffic, latency, per-router status (CPU, memory, temperature, uptime).
- Live topology map: inferred from the bridge FDB (and LLDP when available), with wired/wireless clients, switches and hypervisors detected, WireGuard tunnels drawn peer to Internet.
- Devices: every client with type classification (hostname patterns + OUI), first seen, band, signal.
- WireGuard: peers, latest handshakes, transfer per peer.
- AdGuard Home: query stats and top blocked domains.
- Alerts: temperature, firmware available, new device, handshake, with a bell feed.
- Multi-user auth: bcrypt passwords, per-user language (ES/EN), admin and viewer roles.
- Demo mode (
DEMO_MODE=1): a 67-device sample network, no routers needed. - Optional collector sidecar: TCP latency probes per router with its own long-term time series.
Topology: inferred live from the bridge FDB, tunnels included
Devices: every client classified, with band and signal
Routers: per-router health at a glance
NetPulse is a personal project, built for my own network and released as free software (AGPL-3.0). It is and will always be free. I work on it in my free time: there's a long list of ideas, but little time, and it evolves following my own needs first. With contributions or support it might grow faster, but I can't promise anything. Honest scope note: so far it has only been tested with my own hardware (a GL.iNet Flint 2 gateway and three Xiaomi AX6 access points running OpenWrt) plus WireGuard and AdGuard Home. Other OpenWrt devices should work, but yours would be the first to tell.
| Phase | Status | Highlights |
|---|---|---|
| 1 — Read-only base | ✅ | React PWA, read-only SSH polling (ubus, /proc, iwinfo), AdGuard Home + WireGuard, multi-user auth, LAN discovery, backend migrated from Node to Go (single binary with embedded app) |
| 2 — Topology v5 | ✅ | Real semantic map: live FDB + LLDP, backhaul, managed vs. inferred switches, hypervisors with nested CTs, time-series collector |
| 3 — Alerts, Push & agent (pilot) | ✅ | Alerts with 6 categories and per-category config (urgent/all/none), native Web Push (VAPID), on-demand refresh, switch/bridge audit with reconciled data canon, OpenWrt agent pilot (token ingest, SSH fallback, procd) |
| 4 — Versioned view-model | ✅ | API as a presentation view-model (vm: 1), single-sourced demo canon in Go → JSON, server-stamped Device.infra, semantic topology in the snapshot, Settings revamp |
| 5 — Agent resilience | ✅ | Router-side watchdog + heartbeat, server-side manual rearm, TTL auto-rearm, admin-only mutation routes |
| 6 — Agent security | ✅ | HMAC-SHA256 on agent ingest, serve the agent binary from the server itself |
| 7 — Agent deep dive | ✅ | Real-time wifi events (iw event), bidirectional SSE (AgentHub + refresh), .ipk packaging, profiling (11-12 MB RSS, <1% CPU) |
| 8 — Consolidation | ✅ | Operational metrics in /api/health, persistent agent registry, long-term retention (5-min buckets → 1 year + daily), recharts v3, outgoing alert webhooks (HMAC + retry + DLQ) |
| 9 — Embedded on-router app | 🔮 | On-box server on the gateway (the app URL IS the router's IP), zero-friction pairing, optional luci-app-netpulse |
| 10 — Write/orchestration | 🔮 | Plan → apply → state (Terraform pattern), transactional uci, strict allowlist; starts with AdGuard Home |
| 11 — LuCI package | 🔮 | luci-app-netpulse: local agent status/view (procd, UCI, logs, restart/rearm) + bridge to the web app |
Full detail in docs/ROADMAP.md.
Requirements: Linux (x86_64, arm64 or armv7) with systemd.
curl -fsSL https://raw.githubusercontent.com/gnacho/netpulse/main/install.sh | sh # (recommended)The installer is plain, readable shell: inspect it first. It
detects your distro and arch, downloads the verified release (sha256
against checksums.txt), creates a sandboxed netpulse systemd service
and prints the initial admin password once. Update by re-running the same
line; remove with sh install.sh --uninstall.
Optional latency sidecar (time series of TCP probes to each router):
curl -fsSL https://raw.githubusercontent.com/gnacho/netpulse/main/install-collector.sh | shStable binaries are published per v* tag (goreleaser); rolling per-commit
builds live in the go-latest prerelease for the in-app updater.
The server generates its own ed25519 keypair and shows the public key in
Settings. Authorize it on each router you want to monitor
(/etc/dropbear/authorized_keys). The gateway is auto-detected on first
boot via LAN discovery (TCP :22 sweep, ubus/GL-UI fingerprint); the rest
are added from Settings. Polling is strictly read-only.
# Backend (Go; serves app/dist via go:embed)
cd server-go
cp ../app/dist internal/staticspa/dist -r # the embedded dist is never tracked
go build -o netpulse ./cmd/netpulse && DEMO_MODE=1 ./netpulse
# Frontend (dev server with proxy)
cd app
npm install
npm run devThe legacy Node backend is kept under legacy/server-node/ as history only
(decision 5-Ago-2026: it is not deployed or updated anymore); migration from
its database happens automatically on the first Go boot.
cd server-go && go test ./...