health

Returns the node's operational status and the typed condition catalog that explains it.

Method: GET /health

Parameters: None

Returns: JSON object containing the overall status, network/version information, validator identity (validator mode), and the full condition catalog

HTTP status: 503 when status is unavailable; 200 for every other state.

Example Request:

curl -X GET http://localhost:9153/health

Example Response:

{
  "status": "healthy",
  "network": "",
  "node_version": "v0.6.0",
  "protocol_version": "0.3.0",
  "genvm_version": "v0.0.18",
  "validator_address": "0x1234567890abcdef1234567890abcdef12345678",
  "validator_moniker": "my-validator",
  "conditions": {
    "chain.rpc":                { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "chain.websocket":          { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "chain.sync":               { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:02:14Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "consensus.ban":            { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "consensus.stake":          { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "consensus.operator_funds": { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "consensus.prime":          { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:04:41Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "genvm.llm":                { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:31Z", "updated_at": "2026-09-01T10:29:56Z", "last_ok": "2026-09-01T10:29:56Z" },
    "genvm.web":                { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:31Z", "updated_at": "2026-09-01T10:29:56Z", "last_ok": "2026-09-01T10:29:56Z" },
    "node.disk":                { "state": "healthy", "severity": "critical", "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" },
    "node.snapshot_age":        { "state": "healthy", "severity": "info",     "occurrences": 0, "consecutive_failures": 0, "since": "2026-09-01T08:00:03Z", "updated_at": "2026-09-01T10:31:12Z", "last_ok": "2026-09-01T10:31:12Z" }
  }
}

Response Fields:

  • status (string): the most urgent state across the catalog — unknown, healthy, suspect, warning, degraded, or unavailable
  • network (string): The network the node is connected to
  • node_version, protocol_version, genvm_version (string): build versions
  • validator_address (string, validator mode only): The validator's address
  • validator_moniker (string, validator mode only): The validator's on-chain moniker; reported alongside the address even when empty
  • conditions (object): the full catalog. Every entry is always present — this is not a list of what is broken.
    • state (string): current state
    • severity (string): the catalog's fixed class — info, warning, or critical. Not the state: a critical condition can be healthy.
    • occurrences (number): accepted failed observations since process start
    • consecutive_failures (number): current failure streak; zero after recovery
    • since, updated_at, last_ok (string, optional): ISO 8601 lifecycle timestamps
    • reason (string, present only when not healthy): stable, low-cardinality cause code
    • impact (string, present only when not healthy): what the fault does to this node
    • action (string, present only when not healthy): what the operator should do
    • details (object, present only when not healthy): typed fields explicitly allowlisted by the catalog entry. Raw provider payloads, prompts, credentials, URLs, and stack traces are rejected.

States

StateMeaningAlerted
unknownThe node never observed this condition.no
healthyThe last observation succeeded.no
suspectOne failure, not yet confirmed. It may clear by itself.no
warningThe capability works, and it is confirmed sub-optimal.yes
degradedThe failure is confirmed. The capability does not work.yes
unavailableThe node cannot do its duty at all.yes

Roll-up precedence, most urgent first: unavailable, degraded, warning, suspect, unknown, healthy.

unknown is not on that ladder. It is only ever the state a condition starts in, so it means "not observed yet", never "faulty". A GenVM module rests there until a transaction needs one; node.snapshot_age rests there on a node that takes no snapshots. Letting either gate the endpoint would report unknown for a node where nothing is wrong.

If no condition has been observed at all, status is unknown — nothing observed is not the same as nothing wrong.

Only unavailable returns 503.

A condition can never exceed the liveness cap its catalog entry declares. The GenVM conditions are capped at degraded, so a dead LLM provider returns 200: that node is still booted, synced and voting — it votes Timeout on the transactions it cannot execute, which is the designed behaviour rather than an outage. Failing the endpoint would pull a working validator out of a load balancer.

consensus.ban, consensus.stake, chain.rpc, chain.websocket and chain.sync are capped at unavailable, because each of them stops the node doing its job.

Example: the LLM provider is dead — HTTP 200

{
  "status": "degraded",
  "conditions": {
    "genvm.llm": {
      "state": "degraded",
      "severity": "critical",
      "reason": "llm_request_failed",
      "impact": "This validator cannot execute a transaction that needs an LLM. It votes Timeout, and the consensus contracts may penalize a Timeout vote.",
      "action": "Check the affected LLM provider service and payment or billing status, restore access, or configure a working fallback. If credentials are changed through .env, restart the node.",
      "occurrences": 2,
      "consecutive_failures": 2,
      "since": "2026-09-01T10:30:45Z",
      "updated_at": "2026-09-01T10:31:12Z",
      "last_ok": "2026-09-01T04:12:03Z"
    }
  }
}

Example: the validator is banned — HTTP 503

{
  "status": "unavailable",
  "conditions": {
    "consensus.ban": {
      "state": "unavailable",
      "severity": "critical",
      "reason": "validator_banned",
      "impact": "The consensus contracts do not select this validator for a round. The node earns nothing.",
      "action": "Wait for the stated epoch. If the ban is permanent, restake with a new validator.",
      "details": { "until_epoch": "4182", "permanent": "false" },
      "occurrences": 1,
      "consecutive_failures": 1,
      "since": "2026-09-01T09:14:02Z",
      "updated_at": "2026-09-01T10:31:12Z",
      "last_ok": "2026-09-01T09:13:52Z"
    }
  }
}

chain.sync — 503 only when the node stops syncing

Depth is not a fault. A node syncing from genesis is millions of blocks behind and perfectly healthy. What matters is whether the stored synced block advances.

ReasonStateHTTP
sync_laggingwarning200
sync_catching_updegraded200
sync_stalledunavailable503
sync_feed_frozenunavailable503

The node never skips a duty because it is behind. It votes Timeout only on a transaction that reads state it has not synced; a new contract deploy still runs.

cURL Example with Headers:

curl -X GET http://localhost:9153/health \
  -H "Accept: application/json" \
  -v