Skip to content

feat(node): report per-component health with p2p peer count on GET /status - #25181

Closed
spalladino wants to merge 1 commit into
spl/sequencer-no-peers-gatefrom
spl/status-p2p-health
Closed

feat(node): report per-component health with p2p peer count on GET /status#25181
spalladino wants to merge 1 commit into
spl/sequencer-no-peers-gatefrom
spl/status-p2p-health

Conversation

@spalladino

@spalladino spalladino commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

A validator ran for hours with a dead p2p stack and zero peers while GET /status kept answering a plain 200
with an empty body, so neither operators nor k8s probes had anything to key off. The endpoint now reports the
health of each registered RPC namespace.

GET /status returns a JSON body: { "ok": true, "components": { "<namespace>": { "healthy": true, ... } } },
where each component entry carries its healthy flag plus whatever details its health check reports. The 200/500
semantics are unchanged: 500 when any component is unhealthy, 200 otherwise. Servers with no component-level
checks answer with just { "ok": true }. Health check functions may keep returning a plain boolean, which is
read as { healthy: <boolean> }, so existing checks are unaffected. SafeJsonRpcServer.isHealthy() keeps its
boolean contract; the detailed shape is available through the new getStatus().

The node registers a health check for its p2p namespace that reports enabled and connectedPeers from
getP2PConnectivity(). It is healthy when p2p is disabled by configuration, or when the connected peer count is
at least the new p2pHealthMinPeers p2p option (P2P_HEALTH_MIN_PEERS). That floor defaults to 0, so out of the
box the peer count is reported but never fails the check: the first node of a fresh network legitimately runs
with no peers, and failing readiness by default would prevent bootstrapping the network. Operators who want the
check to fail on a peerless node opt in by raising the floor.

Part of A-1701.


Part of a stacked-PR chain (bottom → top) hardening the node against running with a dead p2p stack; each PR targets the branch below it and the bottom targets merge-train/spartan-v5:

  1. fix(p2p): fail node startup when p2p service fails to start #25177 — fail node startup when the p2p service fails to start
  2. feat(p2p): expose p2p connectivity (enabled + connected peer count) #25178 — expose p2p connectivity (enabled + connected peer count)
  3. fix(slasher): do not file data-withholding offenses while peerless #25179 — do not file data-withholding offenses while peerless
  4. fix(sequencer): skip proposing when node has no connected peers #25180 — skip proposing when the node has no connected peers
  5. feat(node): report per-component health with p2p peer count on GET /status #25181 — report per-component health with p2p peer count on GET /status
  6. fix(node): reject sendTx when node has no peers to propagate the tx #25182 — reject sendTx when the node has no peers to propagate the tx
  7. feat(p2p): warn periodically while node has zero connected peers #25183 — warn periodically while the node has zero connected peers

…tatus

A validator ran for hours with a dead p2p stack and zero peers while `GET /status` kept answering a plain 200
with an empty body, so neither operators nor k8s probes had anything to key off. The endpoint now reports the
health of each registered RPC namespace.

`GET /status` returns a JSON body: `{ "ok": true, "components": { "<namespace>": { "healthy": true, ... } } }`,
where each component entry carries its healthy flag plus whatever details its health check reports. The 200/500
semantics are unchanged: 500 when any component is unhealthy, 200 otherwise. Servers with no component-level
checks answer with just `{ "ok": true }`. Health check functions may keep returning a plain boolean, which is
read as `{ healthy: <boolean> }`, so existing checks are unaffected. `SafeJsonRpcServer.isHealthy()` keeps its
boolean contract; the detailed shape is available through the new `getStatus()`.

The node registers a health check for its `p2p` namespace that reports `enabled` and `connectedPeers` from
`getP2PConnectivity()`. It is healthy when p2p is disabled by configuration, or when the connected peer count is
at least the new `p2pHealthMinPeers` p2p option (`P2P_HEALTH_MIN_PEERS`). That floor defaults to 0, so out of the
box the peer count is reported but never fails the check: the first node of a fresh network legitimately runs
with no peers, and failing readiness by default would prevent bootstrapping the network. Operators who want the
check to fail on a peerless node opt in by raising the floor.

Part of A-1701.
@spalladino

Copy link
Copy Markdown
Contributor Author

Superseded by #25185, which groups this stack's dependent layers (connectivity signal + slasher/proposer/health/sendTx gates) into one PR with a commit per concern, targeting merge-train/spartan-v5 directly. The independent fixes remain as #25177 and #25183.

@spalladino spalladino closed this Aug 12, 2026
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.

1 participant