The config file
A Smart Router config is a single YAML file that lists the listeners the router
opens (endpoints) and the upstreams it routes to (direct-rpc). Use this page to
look up a key in that file.
Both lists are required. A few other top-level keys are optional. You tune everything else (node selection, failover, timeouts) with CLI flags, not the file.
Pass the file to the router as a positional argument:
Working examples live under
config/smartrouter_examples/.
The fastest way to a real config is to copy one and edit it, or to let the
wizard generate one.
A minimal config
metrics-listen-address: "0.0.0.0:7779"
endpoints:
- network-address: "0.0.0.0:3360"
chain-id: "ETH1"
api-interface: "jsonrpc"
direct-rpc:
- name: "eth-publicnode"
chain-id: "ETH1"
api-interface: "jsonrpc"
node-urls:
- url: "https://ethereum-rpc.publicnode.com"
- url: "wss://ethereum-rpc.publicnode.com"
The router pairs each endpoints[] listener with the direct-rpc[] nodes that
share its chain-id + api-interface. A listener with no matching node does not
serve. A node with no matching listener is never used.
Top-level keys
| Key | Required | What it is |
|---|---|---|
endpoints |
yes | The listeners the router opens, one per chain × API interface. |
direct-rpc |
yes | The upstream node pool. |
backup-direct-rpc |
no | Emergency-fallback nodes. The router uses them only after the primary pool is exhausted. Same shape as direct-rpc. |
cache-be |
no | Address of the cache sidecar, e.g. cache:20100. Omit it to disable caching. |
resp-cache |
no | Block that configures a Redis / Valkey cache backend instead of the sidecar. When both are set, it takes precedence over cache-be. |
secondary-cache-be |
no | Address of an optional read-only secondary cache. The router checks it when the primary cache misses. secondary-cache-timeout and secondary-cache-mode tune it. |
metrics-listen-address |
no | Prometheus /metrics address, e.g. 0.0.0.0:7779. disabled turns metrics off. --metrics-listen-address overrides it. |
cross-validation |
no | Per-method operator policies for cross-validation (mandate, cap, or forbid). |
Deprecated keys removed
The old static-providers / backup-providers keys have been removed. Use
direct-rpc / backup-direct-rpc.
endpoints[] - listeners
Each entry opens one listening socket for one chain + interface.
| Field | Required | Description |
|---|---|---|
network-address |
yes | host:port to listen on, e.g. 0.0.0.0:3360. |
chain-id |
yes | Spec chain id (ETH1, SOLANA, COSMOSHUB, …). Must resolve from --use-static-spec. |
api-interface |
yes | jsonrpc, rest, grpc, tendermintrpc, or graphql. The chain spec must support it. A graphql listener accepts HTTP POST only (any path; other methods get 405) and serves no subscriptions. |
tls-enabled |
no | Serve this listener over TLS using a self-signed certificate. Default false. |
health-check-path |
no | HTTP path that returns 200 for liveness probes. Default /lava/health. |
endpoints:
- network-address: "0.0.0.0:3360"
chain-id: "COSMOSHUB"
api-interface: "rest"
- network-address: "0.0.0.0:3361"
chain-id: "COSMOSHUB"
api-interface: "grpc"
direct-rpc[] - upstream nodes
Each entry is one named backend that serves one chain + interface, with one or more URLs.
| Field | Required | Description |
|---|---|---|
name |
yes | Human-readable label. It shows up in logs and as the Prometheus endpoint_id. |
chain-id |
yes | Chain this backend serves. Must match an endpoints[] entry. |
api-interface |
yes | Interface this backend serves. Must match the listener. |
node-urls |
yes | One or more URL entries (below). |
stake |
no | Optional weight (in ulava) for selection scoring. Omit it (or set 0) to apply the default static-node boost. |
group-label |
no | Optional provider-group id (e.g. tier-1, external) for cross-validation group-diversity policies. Has no effect unless a policy requires group spread. |
direct-rpc:
- name: "eth-publicnode"
chain-id: "ETH1"
api-interface: "jsonrpc"
node-urls:
- url: "https://ethereum-rpc.publicnode.com"
- url: "wss://ethereum-rpc.publicnode.com"
node-urls[] - per-URL options
| Field | Description |
|---|---|
url |
Connection URL. Schemes: http(s)://, ws(s)://, grpc(s)://. A jsonrpc backend on an ETH1-derived spec needs a paired wss:// url. A graphql backend's url is the full GraphQL endpoint, path included (e.g. https://graphql.mainnet.sui.io/graphql): the router POSTs to it as-is. |
timeout |
Per-URL request timeout, e.g. 10s. Defaults to the spec value. |
addons |
Add-ons this URL serves, e.g. ["archive", "debug"]. The router only routes matching methods here. |
methods |
Restrict this URL to a specific method list. When set, only those methods route here. |
internal-path |
Path override when proxying through a gateway. |
ip-forwarding |
Forward the client IP via X-Forwarded-For to the upstream. |
skip-verifications |
Skip chain-tracker startup checks for this URL, e.g. ["pruning", "tx-indexing"]. |
auth-config |
Per-URL authentication. See Authentication. |
grpc-config |
gRPC descriptor settings (below). |
accept-encoding |
identity (default) or gzip. gzip asks this upstream to compress its replies. See Compressed upstream replies. |
node-urls:
- url: "https://your-eth-provider.example.com"
timeout: 10s
addons: ["bundler", "debug"]
auth-config:
auth-headers:
x-api-key: "${RPC_KEY_ETH}"
- url: "wss://your-eth-provider.example.com/websocket"
accept-encoding - compressed upstream replies
By default the router asks every upstream for uncompressed replies
(Accept-Encoding: identity). Set accept-encoding: gzip on a URL to ask that
upstream for gzip instead. The router decompresses each reply before it validates,
caches or returns it, so clients get the same response either way. A separate flag,
--response-compression, sets what clients receive.
With gzip, the router spends more CPU and reads fewer bytes. Decompressing costs CPU
on every reply, which is why gzip is off by default. It helps when replies are large
and reading them is the bottleneck. For example, a Solana getBlock reply is several
megabytes of JSON, and gzip makes it four to six times smaller, so the router reads
that much less through TLS and HTTP/2. Measure it against your upstream before you
rely on it. The saving depends on how well that upstream's replies compress, and an
upstream may not compress at all.
node-urls:
- url: "https://solana-mainnet.gateway.tatum.io"
accept-encoding: gzip
auth-config:
auth-headers:
x-api-key: "${RPC_KEY_SOLANA}"
- Values are
identityandgzip, and case does not matter. Any other value stops the router at startup. - Only
http(s)://URLs use it.ws(s)://andgrpc(s)://URLs ignore it. - An upstream that answers uncompressed anyway is read as it is.
grpc-config - gRPC descriptors
These settings apply only to grpc interfaces.
| Field | Default | Description |
|---|---|---|
descriptor-source |
reflection |
reflection (server reflection), file (a compiled descriptor set), or hybrid (reflection, then file). |
descriptor-set-path |
- | Path to a protobuf FileDescriptorSet. Required for file / hybrid. |
reflection-timeout |
5s |
Timeout for reflection queries. |
allow-insecure |
false |
Allow non-TLS grpc://. For dev/test only. |
Cross-validation
This optional top-level block sets per-method operator policies for cross-validation. A policy can mandate it (with floors/caps that clients cannot weaken), or forbid clients from requesting it. Without this block, only clients turn cross-validation on, through the request headers.
cross-validation:
policies:
# Mandate consensus for a sensitive read, capped so clients can't fan out wider.
- chain-id: "ETH1"
api-interface: "jsonrpc"
method: "eth_getLogs"
enabled: true
agreement-threshold: { floor: 2 }
max-participants: { floor: 3, cap: 5 }
# Forbid clients from cross-validating a non-deterministic method.
- chain-id: "ETH1"
api-interface: "jsonrpc"
method: "eth_blockNumber"
forbid-caller-cv: true
| Field | Description |
|---|---|
chain-id / api-interface / method |
Which requests the policy applies to (method casing is preserved). |
enabled |
Mandate cross-validation for this method. |
max-participants / agreement-threshold / min-groups |
Bounds. Each takes { floor, cap }. A client header can raise the value up to the cap, but cannot go below the floor. |
per-group-quorum |
Require each of min-groups groups to reach the threshold on its own (needs min-groups > 1). |
forbid-caller-cv |
Disable cross-validation for this method, even if a client sends the headers. Cannot be combined with enabled. |
Secrets
Never commit API keys. Put them in upstream URLs or auth-config as ${VAR}
placeholders, and render them at run time. See Authentication.
The rendered URL contains the full key and is a bearer credential, so generated
configs go to the gitignored config/local/ by default.
See also
- Authentication -
auth-config,${VAR}secrets, mTLS. - CLI reference - every runtime flag.
- Supported chains - valid
chain-id/api-interfacevalues.