Skip to content

feat(server): surface node health in the mesh snapshot - #1811

Open
clonea1 wants to merge 3 commits into
ruvnet:mainfrom
clonea1:contrib/server-node-health
Open

clonea1 wants to merge 3 commits into
ruvnet:mainfrom
clonea1:contrib/server-node-health

Conversation

@clonea1

@clonea1 clonea1 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

These boards sit on walls with no console attached. Until now a panic, a
watchdog reset, a brownout or a thermal trip was written to a serial port
nobody is reading, so from the operator's side an unhealthy node was
indistinguishable from a healthy one.

The ESP-NOW sync packet already reaches the host every beacon interval and had
reserved bytes free, so the health fields ride along at no extra cost -- no new
socket, no polling, no additional airtime on a channel already at 86%.

Adds to NodeSyncSnapshot:
health the parsed NodeHealth from the sync packet
reset_reason human-readable form, so a dashboard need not carry its own
copy of the esp_reset_reason_t table. Owned rather than
borrowed: the snapshot is Deserialize and a &'static str
cannot round-trip through JSON.
rebooted_badly true for a panic, watchdog or brownout -- the cases worth
surfacing without being asked

Receiver half only, deliberately. A node on older firmware leaves the
formerly-reserved bytes zeroed and simply reports no health, so this is safe to
deploy ahead of the firmware that populates them. The firmware side is a
separate change.


Rebased onto current main before opening: staged before today's seven merges, so it needed replaying to avoid reading as a revert of them. Clean rebase, no files deleted.

Joe and others added 2 commits September 4, 2026 15:02
The v1 ESP-NOW sync packet carries timing only, so a node's identity on the
mesh is inferred rather than stated, and its condition is not reported at all.
Two consequences: the server guesses which node a sync came from, and a node
that is overheating, has rebooted, or is running low on heap looks identical to
a healthy one until it stops responding entirely.

v2 extends the packet to 38 bytes, adding the node's own MAC and a NodeHealth
block: die temperature, thermal state, transmit power, minimum heap seen, and
the reset reason.

The reset reason is the useful part operationally. reset_reason_name() renders
it, and rebooted_badly() distinguishes a panic or watchdog reset from a
deliberate software restart -- a node that reboots quietly and rejoins looks
healthy from the outside, and the distinction is what separates "somebody
updated it" from "it is crashing".

Version-gated so a v1 node keeps parsing exactly as before, and a padded v1
packet explicitly yields no MAC rather than reading whatever happened to sit in
those bytes. Both cases are covered by tests.

Co-Authored-By: claude-flow <ruv@ruv.net>
These boards sit on walls with no console attached. Until now a panic, a
watchdog reset, a brownout or a thermal trip was written to a serial port
nobody is reading, so from the operator's side an unhealthy node was
indistinguishable from a healthy one.

The ESP-NOW sync packet already reaches the host every beacon interval and had
reserved bytes free, so the health fields ride along at no extra cost -- no new
socket, no polling, no additional airtime on a channel already at 86%.

Adds to NodeSyncSnapshot:
  health          the parsed NodeHealth from the sync packet
  reset_reason    human-readable form, so a dashboard need not carry its own
                  copy of the esp_reset_reason_t table. Owned rather than
                  borrowed: the snapshot is Deserialize and a &'static str
                  cannot round-trip through JSON.
  rebooted_badly  true for a panic, watchdog or brownout -- the cases worth
                  surfacing without being asked

Receiver half only, deliberately. A node on older firmware leaves the
formerly-reserved bytes zeroed and simply reports no health, so this is safe to
deploy ahead of the firmware that populates them. The firmware side is a
separate change.

Co-Authored-By: claude-flow <ruv@ruv.net>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants