The owner asked whether remote pollers could work per site over an outbound-only MQTT link, keep polling while the master is down, and receive device credentials sealed to their own key. The assessment found it feasible but deferred it to v2, so the requirements, the gaps in the current code and the main risks are written down for later. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nmon build milestones
The build follows the six gated milestones in docs/spec/spec.md.
Each milestone has its own document here. It is written before any code for that milestone and
its Gate log is filled in when the gate passes. A milestone is done only when:
make lintis clean: maintainability is a hard rule (docs/MAINTAINABILITY.md), and every gate script runs lint first,- every acceptance line it closes (from
build-detail.md) has a test that passes, make test(go test -race ./...) andmake test-integrationpass,- its gate command (
make gate-mN) passes when run by the supervisor, and - the tree is committed and tagged
mN.
| # | Milestone | Gate | Doc | Status |
|---|---|---|---|---|
| M1 | Skeleton and fixtures | nmon serve polls the counters fixture and /api/v1/query returns rising ifHCInOctets |
M1 | passed, tagged m1 |
| M2 | Discovery and profiles | every fixture's expect.yaml passes, including v1 and v3, and the PDU budget holds | M2 | passed, tagged m2 |
| M3 | Graphs and UI | a port's in/out graph renders at 1 h, 24 h and 30 d from the counters fixture | M3 | passed, tagged m3 |
| M4 | Alerting | stopping a parent fixture sends one notification naming it and its children | M4 | passed, tagged m4 |
| M5 | Push ingest, rollup, checks | a replayed Proxmox sample shows under its node and a 2 y query hits the 1 h tier | M5 | passed, tagged m5 |
| M6 | Auth and ops | a viewer gets 403 on PUT (2,000-device loadtest skipped, approved deviation) | M6 | passed, tagged m6 |
| v1.3 | BGP peers and MAC/ARP/FDB lookup | MAC/IP lookup, BGP peers and bgp_peer_down, see the gate list in the doc |
v1.3 | built, gate log filled in (not tagged) |
| M7 | gNMI streaming | after v1; not built in this run | — | out of scope |
How the work is run
- Supervisor (Claude Opus): writes these docs, splits each milestone into work packages (WPs), reviews every diff, runs every gate command itself, commits and tags.
- Implementers (Claude Sonnet sub-agents): one WP each. WPs in different
internal/packages run in parallel. Shared files (go.mod,cmd/nmon/*.gowiring,openapi.json,Makefile) are changed only by the supervisor or by the single WP that owns them in that milestone.
Departures from the spec are recorded in docs/DEVIATIONS.md.
Rules every WP follows (from "Decisions that don't move")
-
Maintainability is a hard rule. Follow
docs/MAINTAINABILITY.md: zero golangci-lint and ESLint findings in your packages, the function, complexity and file-size limits, and the layering rules. Every WP brief cites it. -
One static binary,
CGO_ENABLED=0. No raw SQL outsideinternal/store. -
The 60 s poll path talks only to devices (SNMP, ICMP) and the local hot store.
-
Metric labels on SNMP series:
device=<slug>, plusiface=<ULID>on interface series. Nothing else. -
Every port is polled, up or down. Only the profile's
exclude_ifnamepatterns drop a port. -
Rollups store rates, never raw counters.
-
IPv6 first: every network path works on v6 and v4; tests default to
::1. -
Time comes from
internal/clock. Notime.Now()/time.Sleepin logic that a test must control. -
Tests never need real hardware. Fixtures are
.snmprecfiles served byinternal/simagent.
Deferred (revisit after v1 milestones)
-
Deployment playbook: install layout (binary,
/usr/share/nmon/mibssidecar, systemd unit granting port 162, key file location), TLS/ACME or proxy setup, IdP configuration, backups, and upgrades. Raised by the user on 2026-10-22; deliberately postponed. -
Web push notifications (v2): a
webpushreceiver kind ininternal/notify(RFC 8291 payload encryption, RFC 8292 VAPID, standard-library crypto), per-user subscriptions (needs M6 users), a service worker in the UI, and push endpoints in the outbound allow-list. Out of v1 per the spec's non-goals ("alerts go to webhooks and email only"). Owner deferred it to v2 on 2026-10-22. Until then, a webhook receiver pointed at ntfy or Gotify gives phone push. -
Distributed poller (v2): remote pollers per site or rule that spool to disk while the master is down, send signed chunks over an outbound-only link (MQTT through a broker), and receive device credentials sealed to their own key. Assessed feasible on 2026-10-25 and deferred to v2 by the owner. Requirements, gaps and risks: v2-distributed-poller.md.
-
BMC support: SNMP profiles for Dell iDRAC, HPE iLO and Supermicro (health, temperatures, fans, PSUs, power, RAID) feeding the Health/Power/Environment tabs; a native Redfish collector later. Today, Redfish works only via an exporter pushing with a push token. Owner: "good enough for now" (2026-10-23).
Post-v1 additions
-
Device management UI (2026-10-23): the UI had no way to add a device. Added an admin-only "Add device" dialog on the device list (IPv6/IPv4 addresses, SNMP v1/v2c/v3, synchronous discovery with the reattach 409 flow) and an Actions menu on the device page (Edit with parents and SNMP replacement, Rediscover now, Retire; Purge now asks for the device name). New API route
POST /api/v1/devices/{slug}/retire(admin, audited, additive). The SNMP fields are one shared component (web/src/components/SnmpFields.tsx). Screenshots:docs/screenshots/devices-mgmt/. -
Profiles, profile pinning and row navigation (2026-10-23): new shipped profiles
ubiquiti-edgeswitch,ubiquiti-unifi-switch,ubiquiti-unifi-apandvyos; discovery re-matches unpinned devices every run (and at startup) and an admin can pin a profile from the Edit dialog (DEVIATIONS 39, 40); the device header explains why a device is generic. On the device list, the interface search results and the poller health table, a click anywhere in a row (or Enter on a focused row) opens the device or graph; the name stays a real link. Screenshots:docs/screenshots/v1.0.1/.
Review artifacts
- From M3 on, each UI milestone's gate captures Playwright screenshots of every page into
docs/screenshots/<milestone>/for the user to review.