NetWatch is an MIT-licensed JavaScript single-page application by [@sneak](https://sneak.berlin) that provides real-time network latency monitoring to common internet hosts, displayed with color-coded figures and sparkline graphs, served from a static bucket or Docker container. ## Getting Started ```bash # Install dependencies yarn install # Development server yarn dev # Production build yarn build # Preview production build yarn preview # Docker docker build -t netwatch . docker run -p 8080:8080 netwatch ``` ## Rationale When debugging network issues, it's useful to have a persistent at-a-glance view of latency and reachability to multiple well-known internet endpoints. NetWatch provides this as a zero-dependency SPA that can be deployed anywhere static files are served, with no backend required. ## Design The application is a single-page app built with Vite and Tailwind CSS v4. All code lives in `src/main.js` with a class-based architecture: - **`CONFIG`**: Frozen configuration object (update interval, timeouts, axis ticks, etc.) - **`HostState`**: Per-host state management — history buffer, latency tracking, status transitions - **`AppState`**: Top-level state container — WAN hosts, local hosts, pause state, aggregate stats - **`SparklineRenderer`**: Canvas 2D sparkline drawing with fixed axes, color-coded line segments, error regions, and DPR-aware scaling - **UI functions**: `buildUI()` constructs the DOM, `updateHostRow()` / `updateSummary()` / `updateHealthBox()` handle incremental updates - **`tick()`**: Main loop — measures all hosts in parallel via `Promise.all`, pushes samples, redraws UI. When paused, pushes blank markers (no probes, no false outage) ### Monitoring targets - **11 WAN hosts**: datavi.be, Anthropic API, OpenAI API, AWS, GCP, Azure, DigitalOcean, Cloudflare, Fastly, Akamai, GitHub - **1 Local host**: Local Gateway (192.168.100.1), tracked separately from WAN stats ### Latency measurement HEAD requests with `mode: 'no-cors'` and `cache: 'no-store'`, timed with `performance.now()`. 1-second timeout; anything over 1000ms is clamped to unreachable. IPv4 only. ### Color coding | Latency | Color | | ----------- | ------ | | < 50ms | Green | | < 100ms | Lime | | < 200ms | Yellow | | < 500ms | Orange | | >= 500ms | Red | | Unreachable | Gray | ### Output structure ``` dist/ ├── index.html └── assets/ ├── index-*.css └── index-*.js ``` ## Features - Real-time monitoring with 2s update interval and 300s history sparklines - Health indicator: green (HEALTHY) or red (DEGRADED) based on WAN reachability - Summary stats: reachable count, min/max/avg latency across WAN hosts only - Fixed chart axes: Y-axis 0–1000ms, X-axis 0–300s - Color-coded latency figures and sparkline line segments - Play/pause: pause stops probes but history keeps scrolling (blank gaps, no false outage) - Clickable service URLs - Canvas-based sparkline rendering with devicePixelRatio scaling - Zero runtime dependencies: all resources bundled into build artifacts ## Deployment After running `yarn build`, deploy the contents of the `dist/` directory to any static file host (S3, GCS, Cloudflare Pages, Vercel, Netlify, GitHub Pages) or use the Docker image behind a reverse proxy. The Docker image: - Listens on port 8080 by default (override with `PORT` env var) - Trusts `X-Forwarded-For` from RFC1918 reverse proxies (10/8, 172.16/12, 192.168/16) - Sends access logs to stdout - Caches static assets with immutable headers ## Browser Compatibility Requires a modern browser with ES modules, Fetch API, Canvas API, and CSS custom properties. ## Limitations - **CORS**: Some hosts may block cross-origin HEAD requests. The app uses `no-cors` mode which allows the request but provides opaque responses. Latency is still measurable based on request timing. - **Local gateway**: The 192.168.100.1 endpoint requires the host to be accessible from your network. - **Network conditions**: Measurements reflect browser-to-endpoint latency, which includes your local network, ISP, and internet routing. ## TODO - Add unit tests - Add eslint for JS linting (currently lint target runs prettier only) - Add configurable host list (environment variable or config file) - Add latency history export (CSV/JSON) - Add notification/alert when status changes to DEGRADED ## License MIT. See [LICENSE](LICENSE). ## Author [@sneak](https://sneak.berlin)