A reverse proxy and load balancer for the Antelope SHiP (State History Plugin) WebSocket protocol.
Homepage · Documentation site · Getting started · Frozen SHiP deployment · Configuration · Operations · Observability · Chain API · Dashboard & API · Wire Network · Changelog
Run more than one SHiP node? Put fleet-router in front of them and give your clients (Hyperion, other indexers, custom consumers) one resilient endpoint instead of pinning each consumer to one node.
Clients open a single WebSocket connection to the router. The router picks a healthy upstream, forwards its ABI, and proxies the stream. If that upstream drops, the router fails over to another suitable node, resumes get_blocks at the next block, and de-duplicates blocks the client already has. The client connection stays open.
+-----------------------+
| upstream SHiP node A | (active)
+-----> | ws://hostA:port |
| +-----------------------+
+--------+ WebSocket +--+-----------+
| client | ============> | fleet-router | range-aware / least-connections
+--------+ +--+-----------+ selection + health monitoring
| +-----------------------+
+-----> | upstream SHiP node B | (failover target:
on upstream A failure, | ws://hostB:port | resume at next block,
transparent swap to B ----> +-----------------------+ de-duplicate blocks)
- Failover without reconnecting. On upstream loss, a silent socket or a stalled stream, the session moves to another node, resumes at the next block with the client's outstanding credit, and drops blocks the client already has.
- Range-aware, least-connections routing. Each
get_blocksgoes to an upstream that has the requested block for every log it asks for (traces, deltas, finality data), otherwise to the least-loaded healthy one. Optional per-nodepriorityandweightmark preferred and last-resort nodes and split load. - Stale and lagging nodes are deprioritized, not excluded. Upstreams that stop advancing, or fall behind the fleet head, drop down the list.
- Range-split fleets. Mark archive upstreams
"role": "frozen"and the listener presents live nodes plus frozen range servers (fixed block ranges served from archived logs, no nodeos) as one SHiP node: the router answers status itself, routes each request by block range, and hands the stream from one upstream to the next. See Range-split fleets. - Chain API (opt-in). A nodeos-compatible HTTP port per listener: block lookups go to the frozen range server or live node that holds the block,
get_infoand state calls to live nodes, push and exec are off by default. See Chain API. - Bounded failure on corrupted SHiP logs. A block that breaks every upstream ends the session with close code 1011 and a clear reason instead of a silent stall. Opt-in quarantine skips one node for just the bad block range.
- Several networks in one process. Each listener has its own port, upstream pool,
chain_id, connection cap, and/ready/<name>check. chain_idverification. Upstreams that report the wrong chain stay offline. Older nodes that omitchain_id(FIO and other EOSIO 2.0-based builds) stay usable unless you require it.- Operable. Prometheus metrics, health and readiness endpoints,
tracinglogs, graceful shutdown, upstreamwss://, and connection, timeout and message-size limits. - Live dashboard and control API (opt-in). See every upstream's block range, load and health in real time, and drain a node for maintenance: its sessions move to other nodes without the clients reconnecting.
cargo install fleet-router # from crates.io
docker pull ghcr.io/eosrio/fleet-router # container imagePrebuilt archives for Linux x86_64, macOS (arm64 and x86_64) and Windows x86_64 are attached to each GitHub Release. To build from source:
git clone https://github.com/eosrio/fleet-router.git
cd fleet-router
cargo install --path .fleet-router builds on Linux, macOS, and Windows (x86_64 and arm64).
- Rust ≥ 1.99 (MSRV). Install it with rustup if needed.
- A C compiler and CMake on
PATH, to buildaws-lc-rsfor upstreamwss://. Not needed at runtime.
ABI handling uses the pure-Rust rs_abieos backend, so libclang is not required.
fleet-router config init ./config.json # 1. write a sample config
# 2. list your SHiP nodes in config.json
fleet-router config test ./config.json # 3. validate and test upstream connectivity
fleet-router run --config ./config.json # 4. start the proxyTo start fleet-router at boot and restart it after a crash, run it as a service: systemd, Docker, launchd or Windows.
A minimal config with two SHiP nodes:
{
"listen_address": "0.0.0.0",
"listen_port": 17000,
"upstream_reconnect_ms": 3000,
"upstream_monitoring_ms": 5000,
"upstream_status_ms": 5000,
"servers": [
{ "name": "SHiP Node 1", "endpoint": "127.0.0.1:18080", "enabled": true },
{ "name": "SHiP Node 2", "endpoint": "127.0.0.1:28080", "enabled": true }
]
}A bare host:port is dialed as ws://; use wss://host:port for nodes that serve TLS. Then point your client's SHiP URL at the router (ws://<router-host>:17000) instead of a single node.
On startup the router validates the config and logs its upstream monitors:
configuration is valid.
INFO starting upstream monitor name="SHiP Node 1" upstream=127.0.0.1:18080
INFO listening for clients address=0.0.0.0 port=17000
fleet-router config init <path> Write a sample config file to <path>.
fleet-router config test <path> Parse and validate a config, then test upstream connectivity.
[--strict] Also exit 1 on an invalid config, a coverage hole, an
unreported archive, or archived blocks that no archive
with api_endpoint covers on a chain API listener.
fleet-router run [--config <path>] Run the proxy.
fleet-router check [--port <port>] Exit 0 if 127.0.0.1:<port> accepts TCP (default 17000).
fleet-router --version Print the version.
fleet-router --help Print help.
--config is global and defaults to ./config.json.
Browse the complete documentation site, or read the same guides below.
- Getting started: install, configure upstreams, validate and start your first listener.
- Wire Network: the 0.8.1 ABI fix, upgrade steps and deployment verification.
- Frozen SHiP deployment: deploy Hyperion Tools archive servers and live nodes behind one stitched endpoint. Includes log preparation, configuration, snapshot cut-over and rollout checks. Also available as a site guide.
- Configuration reference: every field and default, topology and stitching, the chain API, upstream TLS, and multi-network
listeners. - Running in production: routing and failover, range-split fleets with frozen range servers, security, running as a service (systemd, Docker, launchd, Windows), graceful shutdown, corrupted SHiP logs, and tuning.
- Observability: logging, health and readiness endpoints,
/coverage, the Prometheus metrics, and alerts. - Chain API: the opt-in nodeos-compatible HTTP port and how it routes requests.
- Dashboard and control API: the opt-in live dashboard, status page, JSON API, and token-protected control actions.
- Example configs:
example.config.json(two full nodes) andexample.stitched.config.json(archived ranges, live nodes and a chain API). - Deployment samples:
deploy/fleet-router.service,deploy/fleet-router@.service, anddeploy/docker-compose.multi-network.yml.
The client listener and optional chain API port are unauthenticated. They default to plaintext ws:// and HTTP; configure client TLS termination for wss:// and HTTPS. TLS encrypts connections but does not add access control. Run fleet-router on a trusted network or behind a reverse proxy that adds access control. Do not expose it directly to the public internet. See Security for details.
To report a vulnerability, follow SECURITY.md. Please do not open public issues for security reports.
Contributions are welcome. CONTRIBUTING.md covers the development setup, the CI checks, and the pre-PR checklist. Tests run against an in-repo mock SHiP double and need no external services. This project follows the Code of Conduct.
Release notes are in CHANGELOG.md (Keep a Changelog format).
Licensed under the MIT License. © EOS Rio.