Three root-level dotfiles diverged from the org models. Two are fixed here; the third is deferred for a reason spelled out below. Move backend/.editorconfig to the repo root. The file is byte-identical to the org model, so this is a pure relocation with no content change. It carries root = true, which one level down was actively harmful: it stopped editors walking further up, leaving the entire frontend (src/, index.html, vite.config.js, nginx.conf, script/) with no indentation settings at all. At the root the same file covers the whole tree, backend included, so the subdirectory copy is redundant. Replace .gitignore with the org model verbatim, then re-append the two repo-specific entries the model does not carry: dist/ (Vite output) and *.log. This adds the OS entry Thumbs.db, the entire Editors section (*.swp, *.swo, *~, *.bak, .idea/, .vscode/, *.sublime-*), and the Environment / secrets section (.env, .env.*, *.pem, *.key). The secrets section is the substantive part. The backend loads .env via godotenv and only backend/.gitignore ignored it, so a .env at the repo root was untracked but unignored -- one git add -A away from being committed. Policy allows no exceptions there. Not done here: excluding .git from .dockerignore. Both images read git metadata at build time. Dockerfile.backend has an explicit COPY .git /repo/.git feeding git describe in backend/Makefile, and the frontend Dockerfile's make check runs vite, whose config calls git rev-parse at config-eval time. Ignoring .git breaks both builds outright rather than degrading them, and decoupling them from git metadata belongs to the Dockerfile rework in #17. Deferred deliberately, not overlooked; tracked separately so it is not lost. No tracked file becomes ignored by the new patterns: git ls-files differs only by the .editorconfig relocation, and check-ignore over the full tracked set matches nothing.
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.