chain#442 phase 3: /v1/cluster/nodes proxy#66
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 3 of ligate-io/ligate-chain#442:
api.ligate.io/v1/cluster/nodesproxies the chain's internal cluster topology to the public, stripping private VPC addresses on the way out and tagging an aggregatecluster_healthfield.What
crates/types/src/lib.rs: adds the wire types — publicClusterTopology/ClusterNode/ClusterHealth(no addresses) plus the internalChainClusterTopology/ChainClusterNode(with addresses) used to deserialize the chain response.crates/indexer/src/client.rs:NodeClient::cluster_nodes(auth_token: Option<&str>)GETs the chain's/v1/cluster/nodes. Sends anAuthorization: Bearer <token>header when configured, matching the Caddy auth gate that chain#442 adds on the gateway VM.crates/api/src/cluster.rs: handler + 5-secondClusterCache+ transformer. Strips per-nodeaddressfields, computescluster_health(healthyif every heartbeat < 2 s,degradedif any > 2 s,leaderlessif no leader is held,unknownif the api can't reach the chain). ReturnsCache-Control: public, max-age=5on the happy path,max-age=10on the degradedunknownpath so partner dashboards back off cleanly during chain outages.crates/api/src/main.rs: route registration.route("/v1/cluster/nodes", get(cluster::nodes)), plusClusterCacheonAppState.crates/api/src/config.rs: new optional env varCHAIN_CLUSTER_AUTH_TOKEN. Unset =cluster_health: "unknown"; set = the api includes the Bearer header on chain fetches.Reachability gate (operator side, NOT in this PR)
The api lives on Railway, outside the chain's VPC. The chain endpoint is Caddy-blocked publicly. Two operator steps need to happen before this proxy starts returning live data:
/v1/cluster/nodes:Environment="CLUSTER_AUTH_TOKEN=..."on the Caddy systemd unit, reload Caddy.CHAIN_CLUSTER_AUTH_TOKENon the Railway api service.Both can land as a follow-up PR + ops change. Until then this proxy ships and returns
cluster_health: "unknown"(cleanly degraded; tested inunknown_topology_has_empty_nodes_and_unknown_health).Public shape
{ \"leader_node_id\": \"ligate-devnet-1-sequencer-2\", \"leader_acquired_at_epoch_ms\": 1779387589432, \"generated_at_epoch_ms\": 1779389534187, \"cluster_health\": \"healthy\", \"nodes\": [ { \"node_id\": \"ligate-devnet-1-sequencer-2\", \"is_leader\": true, \"last_heartbeat_age_ms\": 29 }, { \"node_id\": \"ligate-devnet-1-sequencer\", \"is_leader\": false, \"last_heartbeat_age_ms\": 40 }, { \"node_id\": \"ligate-devnet-1-sequencer-3\", \"is_leader\": false, \"last_heartbeat_age_ms\": 71 } ] }Internal addresses stripped. Caller can branch on
cluster_healthfor a UI badge without parsing the full list.Test plan
cargo test -p ligate-api cluster(5/5 passes — transform strips addresses, healthy/degraded/leaderless/unknown classifications, default empty topology shape)cargo clippy --workspace --all-targets -- -D warningscleancargo fmt --all -- --checkcleancurl https://api.ligate.io/v1/cluster/nodes→ expectcluster_health: \"unknown\"until the Caddy gate + Railway env var landRefs: chain#442.