Skip to content

Configuration Reference

RPC Plane is configured via a TOML file (default: rpc-plane.toml in the current directory).

Run rpc-plane init to generate a starter file. Use -c /path/to/config.toml to specify a different path.

Environment variable interpolation: any value can reference env vars:

url = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"
url = "https://endpoint.quiknode.pro/$QUICKNODE_KEY"   # braces optional

Unset variables expand to an empty string and the proxy logs a warning naming each one — a typo like ${HELIUS_API_KY} therefore surfaces at startup instead of as silent 401s. If you hardcode the full URL and token instead of using a variable, nothing is referenced and no warning is emitted. rpc-plane check reports the same unset-variable warnings before testing connectivity.

The proxy also validates provider entries on startup: provider names must be unique (a duplicate is a hard load error) and every url must be non-empty.


[server]

[server]
listen         = "127.0.0.1:9400"   # address for the proxy (JSON-RPC endpoint)
metrics_listen = "127.0.0.1:9401"   # address for Prometheus metrics + /health
# listen_backlog         = 4096     # OS TCP listen backlog (default 4096)
# pool_max_idle_per_host = 512      # idle outbound connections per provider (default 512)
# worker_threads         = 4        # Tokio worker threads (default: number of logical CPUs)
Key Default Description
listen "127.0.0.1:9400" TCP address the proxy listens on. Change to 0.0.0.0 to expose on all interfaces.
metrics_listen "127.0.0.1:9401" Metrics and health endpoint. Serves GET /metrics (Prometheus) and GET /health (JSON).
listen_backlog 4096 OS TCP listen backlog for both sockets. Raise net.core.somaxconn to match (sysctl -w net.core.somaxconn=4096).
pool_max_idle_per_host 512 Max idle outbound connections kept per provider. Set to at least your expected peak concurrency to avoid cold TCP handshakes under load.
worker_threads number of logical CPUs Number of Tokio runtime worker threads. Set to a fixed count to dedicate cores when sharing a host with other services. Requires a restart to change.

Note

server.listen, metrics_listen, and listen_backlog require a restart to change — they are not hot-reloaded.


[health]

Controls how providers are probed and scored.

[health]
interval_ms            = 1000   # probe each provider this often
window_secs            = 60     # sliding window for error rate
slot_drift_threshold   = 10     # slots behind tip → drifting
circuit_open_failures  = 5      # consecutive failures → circuit open
circuit_error_threshold = 0.5   # error rate threshold → circuit open
circuit_cooldown_secs  = 30     # wait before half-open probe

# Score weights (auto-normalised, do not need to sum to 1)
w_latency = 0.4
w_error   = 0.3
w_slot    = 0.2
w_success = 0.1

Probe settings

Key Default Description
interval_ms 1000 How often (ms) to send a getSlot probe to each provider.
window_secs 60 Sliding window (seconds) over which error rate is computed.
slot_drift_threshold 10 Slots behind network tip before slot freshness score drops to 0.

Circuit breaker

Key Default Description
circuit_open_failures 5 Consecutive probe failures that trip the circuit open.
circuit_error_threshold 0.5 Rolling error rate threshold (0.0–1.0) that opens the circuit.
circuit_cooldown_secs 30 Seconds before attempting a half-open probe after the circuit opens.

Health score weights

Key Default What it controls
w_latency 0.4 Round-trip latency component
w_error 0.3 Error rate component
w_slot 0.2 Slot freshness component
w_success 0.1 Recent probe success rate

See Health scoring for the full formula.


[routing]

[routing]
strategy         = "best_score"   # routing strategy for reads
max_retries      = 2              # retries on transient errors
cost_aware       = false          # enable cost_efficiency_score weight
cost_weight      = 0.2            # w_cost when cost_aware = true
broadcast_writes = false          # fan out write methods to all providers
write_methods    = ["sendTransaction"]  # methods routed as writes
Key Default Description
strategy "best_score" Read routing strategy. See Routing strategies.
max_retries 2 Maximum provider retries on retryable errors (HTTP 429/5xx, RPC -32003/-32005/-32603).
cost_aware false When true, adds a cost efficiency component to the score. Requires [providers.pricing].
cost_weight 0.2 Weight of the cost efficiency score when cost_aware = true.
broadcast_writes false When true, sends every method in write_methods to all healthy providers simultaneously. Maximises transaction landing probability at the cost of N× provider traffic.
write_methods ["sendTransaction"] Methods classified as writes. With broadcast_writes = true they fan out to all providers; otherwise they route sequentially through the failover list. simulateTransaction is not a write by default (it's read-only and not in the landing path) — add it here to broadcast it too.

[[providers]]

At least one provider is required. Add multiple [[providers]] blocks for multi-provider routing.

[[providers]]
name   = "helius"
url    = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"
weight = 1

[[providers]]
name   = "quicknode"
url    = "https://your-endpoint.quiknode.pro/${QUICKNODE_API_KEY}"
weight = 1
http3  = true
Key Required Default Description
name Yes Unique identifier. Must be unique across providers (a duplicate is a load error). Used in logs and metric labels.
url Yes Full HTTP URL including API key. Supports env var interpolation.
weight No 1 Relative weight for weighted_random strategy. No effect on other strategies.
http3 No false Use HTTP/3 (QUIC) for outbound connections to this provider, falling back to HTTP/2 if the negotiation fails. Experimental: depends on a pinned reqwest built with the unstable HTTP/3 feature; treat as best-effort until upstream stabilizes it.
methods No (all) Restrict this provider to a set of JSON-RPC methods. Omit to serve every method. Use it to add a submission-only endpoint (e.g. a transaction-landing service) — methods = ["sendTransaction"] — so it never receives reads it can't answer. Providers that exclude getSlot skip the health probe and are scored from live request outcomes. See Submission-only providers.

[providers.pricing]

Optional cost tracking. Enables cost-aware routing when routing.cost_aware = true.

[[providers]]
name = "helius"
url  = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"

[providers.pricing]
model              = "credits"
monthly_budget_usd = 200
Key Description
model Pricing model: "credits", "compute_units", or "flat".
monthly_budget_usd Monthly budget cap in USD.

[reporting]

Sends telemetry to a remote endpoint. Absent by default — the proxy runs with no outbound connections.

[reporting]
endpoint          = "http://localhost:3000/api/ingest"
api_key           = "rp_live_xxxx"
flush_interval_ms = 60000
Key Description
endpoint HTTP endpoint to POST telemetry batches to.
api_key Bearer token sent with each batch.
flush_interval_ms Aggregation window and POST interval (ms). Minimum 10000. Default 60000.

Full example

[server]
listen         = "127.0.0.1:9400"
metrics_listen = "127.0.0.1:9401"

[health]
interval_ms            = 1000
window_secs            = 60
slot_drift_threshold   = 10
circuit_open_failures  = 5
circuit_error_threshold = 0.5
circuit_cooldown_secs  = 30
w_latency = 0.4
w_error   = 0.3
w_slot    = 0.2
w_success = 0.1

[routing]
strategy    = "best_score"
max_retries = 2

[[providers]]
name   = "helius"
url    = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"
weight = 1

[[providers]]
name   = "quicknode"
url    = "https://your-endpoint.quiknode.pro/${QUICKNODE_API_KEY}"
weight = 1

[[providers]]
name   = "triton"
url    = "https://your-pool.rpcpool.com/${TRITON_API_KEY}"
weight = 1