Review findings on the DNS attribution and hostname series:
- ClientIPOf only trusts EDNS Client Subnet addresses with a full-length
source prefix (32/128); a truncated prefix like dnsmasq --add-subnet=24
carries only the subnet base and is ignored, so attribution stays on the
real client instead of the /24 network address.
- The internal PTR gate now judges the currently selected upstream rather
than the whole pool, so a degraded mixed pool cannot forward internal
reverse lookups to a public fallback.
- Failed hostname lookups are cached for five minutes instead of the full
hour, so a resolver blip stops suppressing names quickly while keeping
retries rare.
- Duplicate IPs in one decoration batch resolve once instead of once per
entry.
- Traffic-view hostname decoration runs on a private copy of the cached
snapshot outside trafficMu, so a stalled resolver neither blocks other
readers nor races on the shared backing array.
- Local DNS replies (failures and empty answers) echo an OPT record when
the query carried EDNS0, per RFC 6891.
The stream handler wrote without a deadline: a client that vanished
without closing the TCP connection (asleep laptop, dropped WiFi) could
block the handler forever once the kernel send buffer filled, leaking
the goroutine and connection on a 24x7 router. Each write now carries a
30s deadline via http.ResponseController and the stream is torn down on
the first failed write; the new failingFlusher test pins the exit path.
Also drop the traffic page's sort/source/client filters from the shared
SSE stream when the page unmounts, so the overview is never served a
hidden page's filtered snapshot, and stop toggling the disconnected
alert during the planned session-renewal reconnect.
Rule listings gain hit counts, last-seen timestamps, and sort by rule
text, hits, or recency; per-rule and rule-miss stats are tracked in
bounded maps fed by router observers. The config console exposes
whitelisted editable fields (immediate for log level and DNS upstreams,
restart-mode for the rest), persisted as pointer overrides. The restart
endpoint closes listeners and execs the process in place (same PID).
Session persistence is best-effort with configurable options, and admin
state/session config gained disable and cookie-secure switches.
- StateStore persists rule deltas and whitelisted config overrides atomically
- Config display/edit API with optimistic concurrency (revision)
- Traffic charts with time-axis linking across all views
- Split server.go into auth/rules/traffic/config by responsibility