Skip to main content

Monitoring Field Glossary

Semantics of the diagnostics surfaced by /state.json and /admin/api/monitor. This file exists because four separate bench incidents produced WRONG mid-incident conclusions from misread fields — each trap below cost real debugging time before a source-check corrected it. Rule: verify a field's semantics here (or in source) before building a monitor or a conclusion on it.

Traps (read these first)​

FieldTrap
pp_* (pp_best_ip, pp_endpoint_count, pp_has_direct, …)Describe the peer at config.priority_peer_ip — on bench remotes that is NOT the machine link. All-zero pp_* on a healthily direct-bonded remote is NORMAL. Do not use as machine-link evidence.
direct_regainsGLOBAL counter across all peers, not per-peer. A regain burst says nothing about WHICH peer regained.
wg_stall_events / derp_stall_events8-deep RINGS. len == 8 means "full", not "8 total" — check the newest entry's at_s for freshness; old entries rotate out silently.
rx_worst_gap_ms / tx_worst_gap_msSince-boot high-water marks that usually SATURATE during the boot/flash reconvergence window. Always read the paired *_at_s before treating a large value as a live event.
nvs_flush_countNVS housekeeping commits — NOT network/conn flushes.
probe_tbl_hwSince-boot high-water of disco probe-table occupancy. Records the size of the REGION that overflowed: 48 = general region full (safety reserve untouched), 64 = true exhaustion including the reserve.
rtt_ms (pstop machine slots)Loop round-trip of the LAST reply — a delayed burst can show tens of seconds without the link being down. Not a path-health signal on its own.
silence_resume_worst_msCheck silence_resume_at_s — boot-window values saturate it. Pairing is per-safety-peer (8 slots).

Crash / reset visibility (/state.json)​

FieldMeaning
reset_reasonesp_reset_reason() of this boot (3 = SW, 5 = INT_WDT, …).
ctrl_reset_causeOne-shot crumb: this boot followed dcs_controlled_reset() — 1 clock-fault, 2 XCHECK, 3 pad-cfg. Zero unless reset_reason is SW.
xcheck_last_detail(fault_code << 4) | stalled_core, rides only an XCHECK-labeled boot. Fault codes: 1 peer task/heartbeat stalled, 2 peer clock frozen/backward.
crash_present / crash_pc / crash_task / crash_shaBoot-time summary of the coredump in flash (persists until the NEXT crash — vintage may predate this boot's build; crash_sha names the crashing build, match it against ops/elf-archive/).
log_lines_s / log_lines_s_peak / log_console_skippedESP_LOG line rate (last full second / since-boot peak / console lines dropped by the 40-line/s budget). Storm context: 147 lines/s was measured at a core-stall; UART0 drains ~115 lines/s at 100 chars — the budget exists so logging can never block a core again.

Lifetime health / wear (/api/health, health in /state.json)​

Counters that survive reboot, idf.py flash and OTA (NVS blob dcs_app/health). Purpose: decide when a unit is retired. Full field list in http_api.md.

FieldMeaning
health (/state.json) / levelWorst active warning: 0 ok, 1 warn, 2 critical. Poll this one field fleet-wide; open /api/health only when it is nonzero.
pressesPhysical stop operations: rising edges of BOTH cores reading STOP. Compare against button_rated_ops (NKK FF01: 100,000).
mismatch_eventsRising edges of the two cores disagreeing on the same tick (one loop open, the other closed). The leading indicator of switch/wiring wear; it climbs before presses gets anywhere near the rating. Distinct from pstop_mismatch in /state.json, which is this boot's comparator count including publish timeouts.
uptime_sCumulative powered seconds. Trap: lags live time by up to CONFIG_DCS_HEALTH_FLUSH_MAX_S (600 s) after a power pull. Not wall-clock; no SNTP involved.
bootsLifetime boot count. Trap: NOT boot_count in /state.json, which is the crash-ladder counter and is zeroed after 120 s of healthy uptime.
flashes / otasBoots on which the image SHA changed / on which the image was OTA-delivered. flashes - otas = cable flashes. A brand-new unit shows flashes: 1.
nvs_flushes / last_flush_age_sThis boot's successful blob writes and the seconds since the last one. last_flush_age_s stuck above 600 with nvs_flush_fails climbing = NVS trouble.
warnings[]One entry per publishing source (src), with count (re-publishes) and age_s (since first raised this boot). Built-in: button_wear, loop_mismatch.

Retire when: level is 2 (button at or past rated life), or loop_mismatch persists after the wiring has been inspected and reseated. Data loss: an erase-flash, a partition-table change that moves NVS, or the boot-time NVS auto-erase on ESP_ERR_NVS_NO_FREE_PAGES/NEW_VERSION_FOUND (an IDF major upgrade) zeroes everything; record /api/health before those.

Path / DERP diagnostics (/admin/api/monitor)​

FieldMeaning
derp_reconnects + derp_reconnect_last_ms/_worst_msHOME-conn reconnect count and durations (RST-to-reconnected). Aux churn is not counted here.
derp_reconn_causes [5]Home-reconnect attribution: [tx write fail, rx read error, tx-active/rx-silent reap, interface rebind, region rehome]. Sum should track derp_reconnects; a persistent shortfall means an unattributed trigger site.
derp_kicks_spacedNon-home connect kicks refused by the 1.5 s spacing gate (flush-storm damping). The home slot and the pool-dark rescue kick bypass refusal.
peer_table_full_countOver-cap peer-add refusals (128-slot table vs the tailnet). Counted always; the WARN log is rate-limited to 1/10 s. Refusals are zero-work: they do not consume add-pacing slots.
disco_silence_pings / silence_resume_last_ms/_worst_ms/_at_srx-silence ping-now machinery: pings sent to direct-silent safety peers, and the last/worst ping→direct-pong resume delta (≈ RTT when healthy).
readd_skipfail[7]Why a peer re-add could not be skipped: [!existing, no-wg-slot, vpn_ip, disco_key, hostname, region, endpoints].

Wire-truth companion​

Firmware self-reported network metrics are not wire evidence (bench rule since 2026-08-09). tools/udp_canary.py is the independent reference: run it on a host behind the same edge; CANARY_REBIND = the public mapping changed (NAT rebind / multi-WAN failover — the run-46 root cause), CANARY_GAP_* = the edge's UDP state was actually down. Correlate its millisecond stamps against the device gauges above before attributing an event to firmware.