netwatch/README.md
sneak cb8d47d7aa Enable prettier prose wrapping for markdown
Add proseWrap: "always" to .prettierrc so markdown prose is
hard-wrapped at 80 columns.
2026-02-23 00:00:25 +07:00

4.4 KiB
Raw Blame History

NetWatch is an MIT-licensed JavaScript single-page application by @sneak 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

# 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

  • 9 WAN hosts: Google Cloud Console, AWS Console, GitHub, Cloudflare, Azure, DigitalOcean, Fastly, Akamai, datavi.be
  • 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 01000ms, X-axis 0300s
  • 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.

Author

@sneak