New
Apps 1,478Makers 1,476This week 1,478
Promote your app−30%
lasso-rpc screenshot 1
1views in
the last 30 days

lasso-rpc

Lasso wrangles your nodes and RPC providers into one fast, resilient, observable JSON-RPC endpoint.

Built with Claude Code/Cursor

by jaxernst · Unclaimed · from USUnited States · added Oct 29, 2026
no ratings0 comments

image

One smart RPC endpoint for all your nodes and providers.

Lasso wrangles your nodes and RPC providers into one fast, resilient, observable JSON-RPC endpoint. It measures every upstream by method and transport, sends each request to the provider best able to serve it, and routes around outages, rate limits, and lagging nodes before your users notice. WebSocket subscriptions survive provider failures, and every routing decision is visible.

Point your client at a Lasso URL instead of a provider's. No SDK, no client library, no application changes.

Self-hosted, Apache-2.0, and built on Elixir/OTP. The bundled public pool covers Ethereum, Base, and Arbitrum (mainnet and Sepolia) with no API keys, so you can try it in one command. Add any EVM chain and your own providers in YAML.

Quick Start · Use it from your app · Configuration · Endpoints · How it works · Migration guide · Documentation · Support

Or get instant access to free curated RPC or hosted BYOK RPC profiles at lasso.sh

Why Lasso

Every RPC provider is a bundle of tradeoffs: latency, uptime, rate limits, archive depth, method support, and cost. Those tradeoffs shift by region, method, and hour, and "compatible" JSON-RPC APIs disagree in the details. A single URL in an environment variable makes all of that your application's problem.

Lasso makes it the RPC layer's problem. Different providers excel at different work: hot reads, archival queries, log scans, subscriptions. Lasso measures that live and routes each request accordingly. You get redundancy without rewrites, and you scale by adding providers instead of replatforming.

What Lasso handles

  • Outages and rate limits. Circuit breakers pull a failing provider from rotation and reads fail over to the next healthy one. Recovered providers are eased back in.
  • Uneven performance. A provider can be fast for eth_call and slow for eth_getLogs. Lasso tracks latency per provider, method, and transport, and fastest sends each call to the quickest provider for that method.
  • Mixed fleets. Declare which providers are pruned, cap log ranges, or skip debug and trace, and Lasso routes each request only where it can be served.
  • Dropped WebSockets. Matching newHeads and logs subscriptions share one upstream. When it fails, Lasso moves to another provider and backfills missed blocks and logs over HTTP.
  • Block height going backwards. With block regression protection, successive latest-block responses never regress, even as providers change.
  • Knowing what happened. Add ?include_meta=body to see the provider, strategy, latency, and retries behind any response, or watch routing live in the dashboard.

Profiles give each app or environment its own providers, chains, and routing from one deployment. Run a node per region and each routes from its own measurements; optional clustering brings every region into one dashboard.

Why not client-side fallback?

Fallback in your client runs separately in every service and process you deploy. Each keeps its own picture of provider health, changing providers means a redeploy, and nothing records which upstream answered. Lasso moves that into one endpoint: shared measurements, one place to change providers, and a record of every routing decision.

Lasso dashboard showing chain topology, provider health, live routing activity, and the request tester

Quick Start

You need Docker with the Compose plugin, curl, and OpenSSL. Start in an empty directory:

mkdir lasso && cd lasso
curl --fail --location https://github.com/jaxernst/lasso-rpc/releases/download/v0.6.0/compose.yml --output compose.yml
(umask 077; printf 'SECRET_KEY_BASE=%s\nRELEASE_COOKIE=%s\n' "$(openssl rand -hex 64)" "$(openssl rand -hex 32)" > .env)
docker compose up -d --wait
curl --fail http://localhost:4000/api/health

Open http://localhost:4000/dashboard. The prebuilt image supports Linux AMD64 and ARM64; no source checkout or build tools required.

Send a request through the fastest strategy and ask Lasso where it went:

curl --fail-with-body --silent --show-error \
  'http://localhost:4000/rpc/fastest/ethereum?include_meta=body' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x18ba3fb",
  "lasso_meta": {
    "strategy": "fastest",
    "selected_provider": {"id": "ethereum_publicnode"},
    "upstream_latency_ms": 45,
    "retries": 0,
    "circuit_breaker_state": "closed"
  }
}

Drop include_meta and the response is ordinary JSON-RPC. Metadata can also arrive as headers or, over WebSocket, as a separate frame; see the API reference.

To watch a live subscription, use the dashboard tester or wscat:

npx --yes wscat -c ws://localhost:4000/ws/rpc/ethereum

At the > prompt, send:

{"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1}

You'll get a subscription ID, then an eth_subscription notification for every new block.

Use it from your app

Any JSON-RPC client works. Swap the provider URL for a Lasso URL:

import { createPublicClient, http, webSocket } from "viem";
import { mainnet } from "viem/chains";

const client = createPublicClient({
  chain: mainnet,
  transport: http("http://localhost:4000/rpc/fastest/ethereum"),
});

const wsClient = createPublicClient({
  chain: mainnet,
  transport: webSocket("ws://localhost:4000/ws/rpc/ethereum"),
});
import { JsonRpcProvider } from "ethers";

const provider = new JsonRpcProvider("http://localhost:4000/rpc/ethereum");

Because the strategy lives in the URL, each client can pick its own: fastest for user-facing reads, load-balanced for background jobs.

Before exposing Lasso

Lasso does not authenticate clients or enforce incoming RPC quotas. Put externally reachable RPC, metrics, and dashboard endpoints behind your network policy or a reverse proxy. The dashboard tester sends real upstream requests.

Compose binds to localhost and keeps profiles and history in a named volume. Preserve .env across restarts. docker compose down keeps your data; --volumes deletes it. See Deployment for verifying the image, custom providers, upgrades, and rollback.

Configuration

Profiles are YAML files. In Docker, follow the host-mounted profile setup; in a source checkout, edit config/profiles/. Keep a valid public.yml and match each additional filename to its slug.

Save this as my-app.yml in your profiles directory:

---
name: My App
slug: my-app
---
chains:
  ethereum:
    chain_id: 1
    providers:
      - id: publicnode
        url: https://ethereum-rpc.publicnode.com
        ws_url: wss://ethereum-rpc.publicnode.com
      - id: drpc
        url: https://eth.drpc.org
        ws_url: wss://eth.drpc.org

Swap in your own nodes and providers. URLs and auth headers support ${ENV_VAR} substitution, and an unresolved variable rejects the configuration instead of starting with a broken provider. Declare capabilities such as unsupported methods, log-range limits, and pruning depth so Lasso routes around them. From a source checkout, mix lasso.probe <provider_url> tests a provider's method support, limits, and WebSocket subscriptions and recommends capability settings. It reports eth_sendRawTransaction and eth_sendTransaction as unverifiable: an invalid payload cannot prove support, and discovery never submits a real transaction. Test transaction behavior with your own controlled workload.

Recreate the container after adding a profile:

docker compose up -d --force-recreate --wait

Then use http://localhost:4000/rpc/profile/my-app/ethereum or ws://localhost:4000/ws/rpc/profile/my-app/ethereum. Later YAML edits apply with a configuration reload; a failed reload keeps the active configuration. Environment or mount changes need a container recreate.

Profiles separate routing configuration; they are not access controls. Profile rps_limit applies to the dashboard tester, not incoming RPC traffic.

Head tracking and provider quota

Lasso tracks every provider's block height so it can tell when one falls behind. That tracking runs on every node and spends provider quota beside your own traffic. With no block_time_ms or cadence overrides, each distinct upstream costs:

Background request Rate per upstream, per node
newHeads notification, when the provider has a ws_url One per block
eth_blockNumber One per 12 s, or one per 36 s while its feed is current
eth_chainId One per 12 s

Successful routed eth_blockNumber and eth_getBlockByNumber("latest") responses also provide head evidence for the provider that served them. A chain needs one head feed, and any provider with a free WebSocket can carry it. Turn background tracking off for providers that bill per request:

providers:
  - id: own-node
    url: http://10.0.0.5:8545
    ws_url: ws://10.0.0.5:8546
  - id: metered
    url: https://rpc.example.com/${METERED_KEY}
    background_observations: false

A provider with background_observations: false keeps serving requests and subscriptions, and Lasso learns its height from the head reads routed to it. Profiles sharing the same upstream share its background work; another enabled reference can keep that work running. The configuration reference lists the intervals and per-provider overrides.

Current limits:

  • Lag filtering needs selection.max_lag_blocks and a reference head that a majority of at least two providers agree on.
  • Head comparison assumes 12-second blocks unless block_time_ms is set, so set it on faster chains.
  • Methods Lasso does not recognize, such as vendor extensions, get one attempt on whichever provider the strategy picks. List them in unsupported_methods on providers that lack them, or send them to /rpc/provider/:provider_id/:chain.

Endpoints

Route HTTP (POST) WebSocket
Default /rpc/:chain /ws/rpc/:chain
Strategy /rpc/:strategy/:chain /ws/rpc/:strategy/:chain
Provider override /rpc/provider/:provider_id/:chain /ws/rpc/provider/:provider_id/:chain
Profile /rpc/profile/:profile/:chain /ws/rpc/profile/:profile/:chain

Strategies are load-balanced (the default), fastest, balanced-fast, which spreads load while favoring the quickest providers, and priority, which tries providers in their configured priority order; latency-weighted is an alias of balanced-fast. Profile routes accept strategies and provider overrides too; see the API reference.

:chain is a configured name such as ethereum or its EIP-155 ID, 1. Routes without a profile use public.

GET /api/health reports liveness and bare GET /api/ready reports replica readiness. Use /api/ready?profile=public&chain=ethereum for strict routing readiness; Deployment describes these probes.

Troubleshooting

Start with container status and recent logs:

docker compose ps -a
docker compose logs --tail=100 lasso
Symptom Check
Container fails to start Look for missing environment variables or invalid YAML in the logs. Keep public.yml; profile filenames must match their slugs.
Port 4000 is in use Set LASSO_PORT=4001 in .env, run docker compose up -d --wait, and use port 4001.
Health passes but RPC fails Health covers Lasso itself, not upstreams. Check provider status in the dashboard, credentials, and network access.
Profile changes have no effect Reload existing profiles on each node. After adding a profile or changing .env or mounts, run docker compose up -d --force-recreate --wait.

Run from source

Use Elixir 1.18.4 and Erlang/OTP 28 (the CI versions), plus Node.js 18 or newer for assets:

git clone https://github.com/jaxernst/lasso-rpc
cd lasso-rpc
mix deps.get
mix assets.setup
mix assets.build
mix phx.server

Open http://localhost:4000/dashboard. See Contributing for tests and code quality checks. To build a production container from the checkout, set SECRET_KEY_BASE and run docker compose up --build -d, or use ./run-docker.sh.

How it works

For each request, Lasso filters providers by method, transport, declared capabilities, and health, ranks the rest with the selected strategy, and executes with retries and failover. Every attempt feeds back into the measurements that drive the next decision and the dashboard. See Routing and Architecture.

A single node works standalone. For global traffic, run a node per region behind geo DNS or a load balancer: each node routes from its own local measurements, so regional latency differences are handled automatically. Optional clustering aggregates observability across regions without touching the routing hot path.

Why Elixir/OTP

RPC routing is a concurrency and failure-handling problem, which is exactly what the BEAM was built for.

  • Massive concurrency: lightweight processes model every request, provider connection, and subscription without shared-memory complexity.
  • Fault isolation: OTP supervision contains failures and restarts components fast, which matters when upstreams are flaky or rate-limited.
  • Fast shared state: ETS serves routing, benchmark, and breaker state on the hot path without a central bottleneck.
  • Distributed by design: clustering and remote messaging are built into the runtime.

Direction

Lasso's routing is built to learn. Provider behavior such as limits, history depth, and method support should be discovered from probes and live traffic, attributed to evidence, and shown to operators instead of hand-written into config. Observations stay typed, so provider comparison can grow from head height to fork-aware checks on headers, logs, and responses.

Documentation

Prefer a managed deployment? Lasso Cloud runs this engine with hosted providers, API keys, and usage analytics. See the Cloud docs.

Contributing

See Contributing for setup and checks. Report bugs through GitHub issues, and open an issue to discuss major changes before sending a pull request. Ownership and review responsibilities are in Maintainers; deployment help is in Support.

Security

See the Security Policy for deployment guidance and private vulnerability reporting.

License

Apache License 2.0. Built by jaxer.eth.

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#rpc · #ethereum · #proxy