.__ .__ _____________________________
_____|__| ____ ____ | | ____\______ \______ \_ ___ \
/ ___/ |/ \ / ___\| | _/ __ \| _/| ___/ \ \/
\___ \| | | \/ /_/ > |_\ ___/| | \| | \ \____
/____ >__|___| /\___ /|____/\___ >____|_ /|____| \______ /
\/ \/_____/ \/ \/ \/
iamkunal9.in
Robust, blazing-fast single RPC for multiple chains with smart load balancing, health checks, continuous retries, and per-request timeouts built in.
- Hyper v1 + hyper-util async server
- Reqwest client for outbound requests
- Round-robin load balancing with simple failure backoff and health tracking
- Continuous retry across endpoints (no 503) until a healthy response is obtained
- Per-request timeout (default 5s, configurable with
-t) - Verbose logging levels with
-v/-vv - CLI flags for config, port, timeout, verbosity
- Library API for embedding the same routing/failover logic in another Rust process
One-liner (downloads the latest release for macOS or Linux and drops the singlerpc binary into /usr/local/bin, falling back to ~/.local/bin if not writable):
curl -fsSL https://raw.githubusercontent.com/iamkunal9/singleRPC/refs/heads/main/install.sh | bashOr build from source:
cargo install --path .Already installed? Upgrade in place:
singlerpc --updatesinglerpc -c path/to/config.json -p 3000
# with custom timeout (seconds)
singlerpc -c config.json -p 3000 -t 10
# verbose logs: endpoints and statuses
singlerpc -c config.json -v
# very verbose: also prints upstream response body
singlerpc -c config.json -vv
# lock the proxy behind a token (clients must send the same token)
singlerpc -c config.json -a supersecret- -c, --config : Path to JSON config mapping chain IDs to arrays of RPC URLs
- -p, --port : Port to listen on (default: 3000)
- -t, --timeout : Per-RPC request timeout (default: 5)
- -a, --auth : Require clients to include the matching token (via
Authorization: Bearer <token>,X-SingleRPC-Auth: <token>, or?auth=<token>); omit to keep the proxy open - --health-file : Persist hard-dead endpoint health in this file (default:
~/.singlerpc/health.json) - --no-health-file: Keep endpoint health in memory only
- -v: Log incoming JSON, endpoint URL, and upstream status
- -vv: Also log upstream response body
- -h, --help: Show help
- -V, --version: Show version
singlerpc can also be embedded as a Rust library, so another daemon can reuse
the Chainlist-backed routing and failover without running a separate HTTP
server.
use singlerpc::{DEFAULT_CONFIG_JSON, RpcProxy, load_config_from_str};
use serde_json::json;
use std::time::Duration;
# async fn example() -> Result<(), Box<dyn std::error::Error>> {
let chains = load_config_from_str(DEFAULT_CONFIG_JSON)?;
let proxy = RpcProxy::with_timeout(chains, 0, Duration::from_secs(10), None);
let response = proxy.request_json("1", json!({
"jsonrpc": "2.0",
"id": 1,
"method": "eth_chainId",
"params": []
})).await?;
assert_eq!(response["result"], "0x1");
# Ok(())
# }The CLI still uses the same library internally.
The bundled Chainlist snapshot includes both mainnets and testnets. Use a custom config file if you want to restrict routing to a smaller set.
Example config.json:
{
"eth-mainnet": [
"https://rpc1.example.com",
"https://rpc2.example.com"
],
"polygon": [
"https://polygon-rpc.example.com"
]
}- The proxy rotates through endpoints in a round-robin loop. If all fail in a pass, it keeps retrying (with short sleeps) until one succeeds.
- JSON-RPC error objects from a healthy upstream are returned to the caller immediately. They are transaction or method errors, not endpoint health failures.
- 429 rate-limit responses get a short in-memory cooldown and are retried later without being persisted as dead.
- Hard endpoint failures such as timeouts, connect errors, body read errors, 5xx responses, or invalid upstream responses are marked dead after 3 consecutive failures. The CLI persists hard-dead endpoints for 24 hours in
~/.singlerpc/health.jsonby default, then retries them.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.