# Developer notes ## Building the UI into the binary `make build` runs `make web` first: `npm run build` in `web/`, then `web/dist` is copied to `internal/webui/dist`, which `//go:embed` packs into the binary. `internal/webui/dist` is git-ignored (only `.gitkeep` is tracked), so a build never dirties the tree. A plain `go build` without `make web` embeds a one-line "UI not built" page from `internal/webui/placeholder/`. nmon serves the UI at `/`. Unknown paths fall back to `index.html` for the client-side router, except `/api/`, `/docs`, `/openapi*`, `/metrics` and `/schemas`, which belong to the API. Files under `/assets/` are content-hashed and sent `immutable`; `index.html` is `no-cache`; text assets are served gzipped. ## OpenAPI compatibility `scripts/openapi-check.sh` compares the current spec (`nmon openapi`, else `openapi.json`) with `openapi.json` at the latest `m*`/`v*` tag and fails on a removed route or method, a removed response field, a changed field type, a new required parameter or request field, or a removed enum value. A tag without `openapi.json` passes with a note. `OPENAPI_NEW=file.json` compares that file instead and `OPENAPI_BASE_TAG=m3` picks the base. The comparison is `internal/openapidiff`. ## `nmon-devtools` (separate dev binary) Developer helpers in their own executable, `make devtools` -> `nmon-devtools`; the shipped `nmon` does not contain them and never links the simulator. They need a running `nmon serve --insecure-http`. **backfill** writes synthetic history straight into the hot store so graphs have 30 days of data without waiting 30 days: nmon-devtools backfill --hot-url http://127.0.0.1:PORT --device SLUG --iface ULID [--iface ULID...] \ [--days 31] [--step 60s] [--pattern diurnal|flat] [--mean-mbps N] Per interface it writes `ifHCInOctets`, `ifHCOutOctets` (monotonic, diurnal rate with noise and bursts) and `ifOperStatus`, ending one step before the first live sample. When live samples exist the history is shifted so its last value joins the live counter with no reset (this needs a live counter larger than the history, about 3e13 bytes for 31 days at 100 Mbit/s; otherwise a warning says the join will look like a reset). The hot URL is in the serve log (`metric stores up hot=...`). Samples older than hot retention are dropped by the store. **seed-demo** builds a small simulated network and back-fills it, for the gate and for screenshots: nmon serve --data-dir D --insecure-http --poll-interval 10s ... nmon-devtools seed-demo --api http://127.0.0.1:8443 --hot-url http://127.0.0.1:PORT \ [--env-out env] [--counters-rec testdata/snmprec/counters.snmprec] It starts the simulated SNMP agents in-process on `[::1]`, each on its own port (passed to the API as `snmp.port`), adds the devices through the API (a 48-port switch, access switches, routers, a firewall, a UPS, the counters fixture, a v1 device with 32-bit counters, and one whose agent is stopped after seeding so it reads as down), waits for live polls, back-fills 31 days, writes `NMON_DEVICE`, `NMON_DEVICE_48`, `NMON_DEVICE_32BIT` and `NMON_DEVICE_DOWN` to `--env-out`, and keeps the agents answering until interrupted. The simulated 64-bit counters start at 1e14 so history fits below them. ## Gates `make gate-m3` runs everything above end to end and writes live screenshots to `docs/screenshots/M3/`. It needs Playwright's chromium: `cd web && npx playwright install chromium`.