backend/.golangci.yml declared version: "2" on line 1 but used the golangci-lint v1 schema below it: a top-level linters-settings key and an issues.exclude-use-default key that does not exist in v2. Under v2 that config does not validate, so every threshold in it was inert -- lll fell back to its 120-column default rather than the intended 88, and funlen, cyclop and dupl were not applied at all. The `0 issues.` result the repo has been relying on was therefore meaningless. Replace it with the org-standard file verbatim (sha256 021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb) and repoint the golangci-lint pin in Dockerfile.backend from v2.7.2 to the org-standard v2.12.2. Guard against the config drifting from the standard again by asserting its sha256 as the first step of the backend lint target. The check is a local hash comparison against a constant in the Makefile: it needs no network, fetches nothing, and adds no unpinned external reference to the build path. It also catches a strictly larger class of breakage than schema validation would, since a schema-valid but non-canonical config is exactly how this file got into its broken state. With the config actually loading, lll reports server.go:65 at 93 columns. Fix it, plus the two other over-long lines called out on the issue (server.go:97 at 81 and reportbuf.go:166 at 88) which are inside the 88-column lint limit but over the 77-column hard wrap in the Go styleguide. All three were long //nolint justifications on the code line; move the justification into a preceding comment block and leave a short directive behind. No suppression is added or widened, and .golangci.yml is not touched after the copy. Drop the //nolint:wsl in server.go entirely rather than relocating it. The standard config disables wsl, so the directive suppressed nothing; removing it still yields `0 issues.` Verified: `cd backend && make check` reports `0 issues.` and passes with the network unavailable, root `make check` passes, and `docker build -f Dockerfile.backend .` builds green against the pinned v2.12.2.
netwatch-server is an MIT-licensed Go HTTP backend by @sneak that receives telemetry reports from the NetWatch SPA and persists them as zstd-compressed JSONL files on disk.
Getting Started
# Build and run locally
make run
# Run tests, lint, and format check
make check
# Docker
docker build -t netwatch-server .
docker run -p 8080:8080 netwatch-server
Rationale
The NetWatch frontend collects latency measurements from the browser but has no
way to persist or aggregate them. This backend provides a minimal
POST /api/v1/reports endpoint that buffers incoming reports in memory and
flushes them to compressed files on disk for later analysis.
Design
The server is structured as an fx-wired Go application under cmd/netwatch-server/.
Internal packages in internal/ follow standard Go project layout:
config: Loads configuration from environment variables and config files via Viper.handlers: HTTP request handlers for the API (health check, report ingestion).reportbuf: In-memory buffer that accumulates JSONL report lines and flushes to zstd-compressed files when the buffer reaches 10 MiB or every 60 seconds.server: Chi-based HTTP server with middleware wiring and route registration.healthcheck,middleware,logger,globals: Supporting infrastructure.
Configuration
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP listen port |
DATA_DIR |
./data/reports |
Directory for compressed reports |
DEBUG |
false |
Enable debug logging |
Report storage
Reports are written as reports-<timestamp>.jsonl.zst files in DATA_DIR.
Each file contains one JSON object per line, compressed with zstd. Files are
created with O_EXCL to prevent overwrites.
TODO
- Add integration test that POSTs a report and verifies the compressed output
- Add report decompression/query endpoint
- Add metrics (Prometheus) for buffer size, flush count, report count
- Add retention policy to prune old report files
License
MIT. See LICENSE.