# Performance benchmarking Source: https://docs.lasso.sh/advanced/benchmarking Distinguish recent routing evidence from operational performance summaries In **Lasso RPC Core v0.5.0**, request execution supplies performance observations without requiring a separate benchmark request for every user call. Background health and head probes are separate upstream traffic. Measure their cost alongside your workload when qualifying a deployment. ## Routing evidence and reporting Recent routing evidence and dashboard summaries serve different purposes: | Surface | Purpose | | - | - | | Recent routing evidence | Reliability qualification and successful-attempt latency used by latency strategies | | BenchmarkStore | Operational call, outcome and latency summaries | | Request metadata | Evidence about one request's candidates, attempts and successful channel | Routing evidence is local to the node and scoped to the route, transport and a bounded registered workload key. A lifetime average or a high aggregate success rate does not substitute for recent qualified evidence on the relevant route. Provider aliases, shared upstreams and multiple nodes also affect how counts should be interpreted; do not add overlapping summaries as independent work. ## How strategies use evidence **Fastest** orders reliability-qualified candidates by recent successful mean latency, with successful p95 and identity tie-breakers. Unqualified candidates remain as fallbacks. Missing and stale evidence do not establish reliability. **Latency weighted** uses relative weights `(best_mean / candidate_mean)^beta` and exponential-race ordering `-log(U) / weight`. Reliability is a qualification boundary, not a success-rate multiplier. There is no weight floor or hidden exploration share. Available latency priors can still order unqualified fallbacks; unmeasured channels are shuffled. **Load balanced** starts from a randomized order and gives distinct physical instances a [bounded first pass](/concepts/routing-strategies#load-balanced) for replay-safe calls within each health tier. It does not rank by a benchmark score. **Priority** uses configured order. All strategies apply [health tiering](/concepts/routing-strategies#health-based-tiering) and live admission. The supported latency control is `LW_BETA` (positive number, default `3.0`). See [routing strategies](/concepts/routing-strategies) for the full released contract. ## Interpreting measurements Keep chain, profile, transport, workload, source revision, time window and sample count with any latency result. Separate successful-attempt latency from end-to-end request latency: retries, selection and internal work can make them differ. Use `attempted_channels` and `executed_channel` in [request metadata](/observability/request-metadata) to understand a specific result. A percentile computed from one sample set cannot be reconstructed by averaging percentiles from other nodes. Report local percentiles or retain compatible raw samples for an aggregate calculation. Missing observations are unknown, not zero latency or successful service. ## Retention and overhead In v0.5.0, BenchmarkStore periodically trims raw per-chain samples. A burst can exceed that threshold before the next trim, and aggregated provider/method score entries have no fixed cardinality cap in this release. It is not a durable complete request journal. Monitor process and ETS memory for your configured profile, method, and provider mix. The released [BenchmarkStore source](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/benchmarking/benchmark_store.ex) defines the exact behavior; this page does not assign a fixed per-call overhead or memory footprint to every deployment. The historical [routing-overhead report](https://lasso.sh/benchmarks/routing-overhead.html) is a dated engine benchmark. Preserve its source and environment attribution; it is not v0.5.0 hosted capacity or an SLA. [Versions and evidence](/releases-and-availability) links the current release acceptance evidence. Implementation references: [Fastest](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/selection/strategies/fastest.ex), [LatencyWeighted](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/selection/strategies/latency_weighted.ex), and [routing evidence](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/routing_evidence.ex). # Block regression protection Source: https://docs.lasso.sh/advanced/block-continuity Protect latest-block responses with automatic peer sharing. Turn on block regression protection for a chain, and Lasso protects successive latest-block responses and shares progress across connected instances. It's off by default. | Product | How to turn it on | | - | - | | RPC Core | Set `head_policy: local` on the chain in its file profile. Local needs no publication database | | Lasso Cloud | Turn **Block regression protection** **On** in your Custom profile's chain settings | Applies to: * `eth_blockNumber` with `[]`. * `eth_getBlockByNumber` with `["latest", false]` or `["latest", true]`. ## What to expect After a protected response returns block 100, the next request on the same running Lasso server returns **100 or higher, or an error**, even if the upstream provider changes. The next request must start after the previous response finishes; overlapping requests can finish out of order. All callers using the same profile and chain share this protection. After a server switch or restart, recovery is **best effort**: a lower height remains possible. Provider lag, stale blocks or reorganizations can cause retries or errors. Retries and connection setup can add latency. Protected blocks must be within **60 seconds or four configured block intervals**, whichever is greater; protection does not promise the newest block or finality. ## Reading state Ordinary `latest` balance and contract calls remain unpinned. Explicit block targets stay unchanged through retries, including older blocks. To read several values at one block, follow [Read at one block](/advanced/read-at-one-block); that works with protection Off too. [Routing metadata](/observability/request-metadata#block-protection-metadata) is optional. For the separate durable `global` contract in RPC Core, see [fleet operations](/deployment/block-continuity). Enrolled Global scopes require coordinated disablement before changing modes. On Lasso Cloud, existing `global` profiles keep strict fleet coordination until protection is turned Off; turning it On again enables the standard behavior above. # Block height monitoring Source: https://docs.lasso.sh/advanced/block-sync How RPC Core observes upstream heads and uses them in routing. RPC Core v0.5.0 observes provider block heights so routing can account for lag. It polls each configured upstream over HTTP and, when enabled and connected, also receives `newHeads` over WebSocket. These observations help select a route; they do not establish chain consensus or guarantee that a provider can serve every historical request. For request-level monotonicity, use [Block regression protection](/advanced/block-continuity). For related state reads at one block, use [Read at one block](/advanced/read-at-one-block). ## HTTP and WebSocket observations Each physical upstream instance has one block-sync worker for a chain, even when multiple profiles refer to it. The worker stores height, observation time, and source in the block-sync registry. Profiles sharing that instance also share its observations. HTTP `eth_blockNumber` polling runs while the worker is active. The effective interval comes from the shortest configured `monitoring.probe_interval_ms` among referencing profiles. When a WebSocket `newHeads` subscription is active, HTTP polling continues at three times that interval. The normal interval returns if the subscription becomes unavailable. WebSocket updates can arrive sooner than the next poll, but neither transport guarantees a fixed observation delay. The chain-level `websocket.subscribe_new_heads` setting defaults to `true`. Providers inherit it unless they set their own `subscribe_new_heads` value. A usable WebSocket connection is also required. See [Profile configuration](/configuration/profiles) and [Provider configuration](/configuration/providers). ## Comparing heights Core ignores stale observations when deriving its chain reference height. With a measured block interval, it may give a bounded credit to an older observation before comparing heights. The reference is selected from the current, aligned provider samples; it is an operational routing estimate, not a consensus vote or proof of the canonical chain. A provider's lag is compared with that reference. If a profile sets `selection.max_lag_blocks`, sufficiently lagging candidates can be excluded. Unknown or stale data must not be read as verified head agreement. The dashboard's `monitoring.lag_alert_threshold_blocks` is a separate status threshold; it does not set the routing limit. The measured block interval uses an exponential moving average of increasing heights. Until five valid samples are available, Core uses the configured `block_time_ms` fallback. This helps account for observation delay on fast chains, but it cannot turn a stale or incorrect upstream response into a verified block. ## Health probes The probe coordinator checks upstream HTTP identity and health separately from block-height polling. Its 200 ms tick schedules work; it does not probe every provider five times per second. The effective provider cadence follows the shortest configured probe interval among profiles sharing that instance, with failure backoff and jitter. A matching `eth_chainId` probe can restore an HTTP endpoint previously rejected for an identity mismatch. See [Provider selection](/concepts/provider-selection) for the remaining admission checks. ## Operating guidance * Configure `probe_interval_ms`, `block_time_ms`, and lag limits for the chain's actual cadence and your workload. Sharing a provider across profiles can change its effective polling cadence. * Compare provider heights and freshness by region before treating lag as a provider fault. A quiet log stream alone is not evidence of a dead connection. * Verify a representative upstream-backed request; a healthy application endpoint does not prove that every provider is usable. The released [block-sync worker](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/block_sync/worker.ex), [registry](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/block_sync/registry.ex), [lag calculation](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/providers/lag_calculation.ex), and [probe coordinator](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/providers/probe_coordinator.ex) are the implementation references for v0.5.0. # Error classification Source: https://docs.lasso.sh/advanced/error-classification How released RPC Core v0.5.0 decides failover and circuit penalties. **Lasso RPC Core v0.5.0** classifies upstream responses and transport failures to decide whether another provider may be tried and whether a transport's circuit breaker should be penalized. Classification is internal routing behavior. A valid, request-correlated upstream JSON-RPC error retains its original `code`, `message`, and optional `data` in the caller response. ## Decision order The released [classifier](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/support/error_classification.ex) checks structural revert evidence and definitive codes before message patterns. For ambiguous codes, it uses bounded message evidence for execution errors, provider limits, authentication, and capabilities, then falls back to the code. For example, a provider may use `-32603` for a capability restriction; that code alone is not a definitive internal-error verdict. An EVM revert is a request-caused outcome, even when provider wording also mentions a limit. ## Failover and breaker behavior | Category | Try another provider for a replay-safe request? | Penalize the circuit? | | - | - | - | | `rate_limit`, `capability_violation`, `block_not_available`, `requires_archival`, `method_not_found` | Yes | No | | `network_error`, `server_error`, `internal_error`, `auth_error`, `chain_error`, `timeout`, `provider_error`, `method_error` | Yes | Yes | | `invalid_request`, `invalid_params`, `parse_error`, `client_error`, `user_error`, `execution_revert` | No | No | | `unclassified_server_error`, `unknown_error` | No by the category's direct retry rule | No | The table describes the classifier's category functions. Request execution also applies method safety, transport policy, remaining deadline, candidate admission, and dispatch limits. A category marked “Yes” does not authorize replaying a signed transaction or exceeding its one-dispatch budget. See the [method contract](/api/supported-methods) and [routing strategies](/concepts/routing-strategies). ## Provider-specific rules Profile `capabilities.error_rules` can classify a provider's observed code or message. Use the narrowest supported match and qualify it with your upstream: provider wording and plans change. Rules affect routing and health decisions, so a broad false match can either cause unnecessary failover or hide a real provider failure. See [provider capabilities](/configuration/capabilities). ## Inspecting a failure Request [metadata](/observability/request-metadata) to see recorded channels and categories where available. A candidate list is not a dispatch log, and missing attempt metadata is unknown rather than proof that no dispatch occurred. Keep the request ID, method, profile, chain, original error, and source version when investigating a discrepancy. Do not log provider credentials or full URLs. # Read at one block Source: https://docs.lasso.sh/advanced/read-at-one-block Use one block hash for related state reads, on RPC Core or Lasso Cloud. Separate `latest` calls can read different blocks. Fetch one block, then reuse its hash for every related state read. This standard JSON-RPC pattern works with **Block regression protection Off**. Use your client's `request()` helper, which returns `result` and throws on RPC errors. Set `address` to the account you want to read. ```js theme={null} const block = await request({ method: "eth_getBlockByNumber", params: ["latest", false], }); if (!block?.hash) throw new Error("Block unavailable"); const at = { blockHash: block.hash, requireCanonical: true }; const [balance, nonce] = await Promise.all([ request({ method: "eth_getBalance", params: [address, at] }), request({ method: "eth_getTransactionCount", params: [address, at] }), ]); console.log({ blockHash: block.hash, balance, nonce }); ``` Both values describe the selected block; the nonce excludes pending transactions. Reuse `at` for contract calls and dependent reads too. In a JSON-RPC batch, put the selector on **every item**. ## Essential limits * Providers must support [block-hash selectors](https://eips.ethereum.org/EIPS/eip-1898) and retain the requested state. Lasso preserves the hash through retries. * Lasso sends block-hash state reads only to archival providers, because a hash's age is unknown before dispatch. In RPC Core, a chain with no provider declared archival rejects them with [`archive_required`](/api/error-codes#core-routing-exhaustion); on Lasso Cloud, a profile without one fails them instead of reaching a provider that may lack the state. * `requireCanonical: true` asks the provider to reject a block it knows was removed from the canonical chain. It does not guarantee finality or prove execution correctness. * If a read fails and you choose a new block, repeat all related reads. Do not mix results from different hashes. For historical reads, resolve a block number to a hash once. Use `"finalized"` instead of `"latest"` when you need the chain's finality semantics and your provider supports it. [Block regression protection](/advanced/block-continuity) separately protects successive latest-block requests. [Routing metadata](/observability/request-metadata) is optional. # WebSocket subscriptions Source: https://docs.lasso.sh/advanced/websocket-subscriptions Operate and qualify bounded subscription recovery in RPC Core v0.5.0. Lasso RPC Core v0.5.0 multiplexes matching `newHeads` and `logs` streams and attempts bounded recovery when an upstream fails. An open socket or a successful subscription response does not prove that your application received every event. Use [WebSocket endpoints](/api/websocket-endpoints) for connection examples and [Supported methods](/api/supported-methods) for the released method contract. ## Configure the provider pool Configure both HTTP and WebSocket endpoints for providers that support your chain and workload. Providers inherit the chain's `subscribe_new_heads` setting, which defaults to `true`; set a provider override to `false` if it must not serve `newHeads`. A usable WebSocket connection is still required. See [Provider configuration](/configuration/providers). Identical subscriptions can share upstream work. Different log filters can create distinct streams, and HTTP replay consumes upstream requests. A high client-to-upstream sharing ratio does not establish capacity for many distinct filters or dense logs. Shared resources do not create quota isolation. ## Recovery behavior Recovery establishes a replacement live subscription, buffers incoming events, and uses HTTP to replay the missing range. HTTP replay and WebSocket delivery can use different eligible upstreams. The coordinator merges replay with its buffered live events and suppresses overlap within retained deduplication state. Recovery has a deadline, a replay allowance, an attempt budget, and a memory bound. Buffer exhaustion terminates the affected downstream connection; it does not silently discard the oldest buffered event and claim uninterrupted delivery. Unavailable history, failed replay, or exhausted recovery can also terminate continuity. The application must reconnect and reconcile its durable checkpoint. Use the [v0.5.0 configuration reference](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/CONFIGURATION.md) for supported configuration keys. The released [stream coordinator](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/streaming/stream_coordinator.ex) is the implementation reference. Internal process defaults are not necessarily YAML settings, and increasing a limit cannot supply missing provider history. ## Application checkpoints and reorgs Persist the last successfully processed block and its hash outside the socket connection. Handle reconnection and `removed` log events. Reconcile your state against canonical blocks after an outage or reorg; block height alone cannot distinguish a replacement block from the one your application processed. In v0.5.0, buffered orphan-log additions and removals drain in their original per-log order before replacement-block additions, including across block heights. Positive and removed events have separate deduplication state. Repeated removal and re-addition of the same log within that window is not fully modeled; reconcile against canonical state when that sequence occurs. Do not infer lossless or exactly-once processing from multiplexing or replay. Verify the received sequence for removed and replacement logs, replay/live overlap, and sparse filters against the exact released artifact you deploy. Keep application-fetched repairs separate from events delivered by Lasso when reporting recovery results. ## Qualify your workload Before production adoption, exercise the intended chain, provider pool, filter cardinality, event rate, and downstream consumption rate: 1. Confirm real block or log notifications after establishment. 2. In an isolated environment with test-owned upstreams, interrupt the active upstream and retain received frames through recovery. 3. Compare frames with the expected blocks and logs, including removals and replacement events during a controlled reorg. 4. Exercise slow consumers, unavailable HTTP history, and recovery exhaustion. Confirm your client notices termination and restores its checkpoint. 5. Restart or replace your Lasso instance and measure application recovery. Record first failures, missing ranges, duplicate/removal ordering, recovery duration, application repairs, and upstream request volume. Test fast chains separately: the same number of blocks represents a shorter outage window. No general failover-latency or subscription-capacity number is established by the routing HTTP microbenchmark. See [Versions, availability, and evidence](/releases-and-availability) for the public installation checks and their limits. Lasso Cloud has a separately deployed [workload contract](/cloud/rpc-behavior); do not assume stream recovery changes reach a Core release at the same time. # Authentication Source: https://docs.lasso.sh/api/authentication Authentication and access control for Lasso RPC ## Current Status RPC Core has no built-in incoming client authentication. Its endpoints are reachable by anyone who can access your deployment's network address. If you deploy Lasso publicly, ensure proper network-level security (firewall rules, VPC isolation, reverse proxy authentication) to restrict access. ## Securing Your Deployment Since Lasso doesn't have built-in authentication, use these strategies to secure your deployment: ### Reverse Proxy Authentication Deploy Lasso behind a reverse proxy (nginx, Caddy, Traefik) with authentication. Apply the boundary to every exposed RPC, WebSocket, dashboard, and operational route. Core v0.5.0 exposes JSON metrics at `/api/metrics/:chain` and Prometheus text at `/metrics`; it does not expose a management API. For nginx, keep the authentication at the site level so it also covers WebSocket upgrades: ```nginx theme={null} map $http_upgrade $lasso_connection_upgrade { default upgrade; '' close; } server { listen 443 ssl; server_name rpc.example.com; ssl_certificate /etc/letsencrypt/live/rpc.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/rpc.example.com/privkey.pem; auth_basic "Lasso RPC"; auth_basic_user_file /etc/nginx/.htpasswd; location / { proxy_pass http://127.0.0.1:4000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $lasso_connection_upgrade; proxy_read_timeout 7500s; } } ``` Replace the hostname, certificate paths, and password file. Keep Core bound to loopback or a private network address so clients cannot bypass the proxy. A collector scraping the JSON metrics API must authenticate too. ### API Gateway Use an API gateway (Kong, Tyk, AWS API Gateway) to add: * API key validation * Rate limiting per key * Usage tracking * Multiple authentication methods ### Network-Level Security * **Firewall rules**: Restrict access by IP address * **VPC isolation**: Deploy in private subnet, expose via load balancer * **VPN**: Require VPN access to reach Lasso endpoints * **mTLS**: Client certificate authentication at load balancer ## Provider Authentication Lasso authenticates with upstream RPC providers using API keys configured in your profile YAML: ```yaml theme={null} providers: - id: "alchemy_eth" url: "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}" ``` Set provider API keys as environment variables: ```bash theme={null} export ALCHEMY_API_KEY=your_alchemy_key export INFURA_API_KEY=your_infura_key ``` See [Environment Variables](/configuration/environment-variables) for how profile files substitute variables supplied by your deployment. Core's self-hosted boundary does not include managed accounts, billing, or incoming client keys. Use [Lasso Cloud](/cloud/overview) when you need those managed services. ## Best Practices Deploy in private network, access via VPN or bastion host Use reverse proxy with authentication and rate limiting Bind to localhost only: `http: [ip: {127, 0, 0, 1}]` Layer multiple security controls (network + proxy + monitoring) ## See Also * [Deployment](/deployment/production-checklist) - Security checklist * [Configuration](/configuration/environment-variables) - Provider API keys * [Docker](/deployment/docker) - Container security best practices # Batch requests Source: https://docs.lasso.sh/api/batch-requests Independent JSON-RPC item execution within one HTTP request. Send a JSON array to an HTTP RPC endpoint: ```bash theme={null} curl --fail-with-body http://localhost:4000/rpc/ethereum \ -H 'Content-Type: application/json' \ -d '[{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1},{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":2}]' ``` The default maximum is 50 items (`max_batch_requests`). Each item is validated and routed independently with bounded concurrency and its own execution deadline. Results appear in request order. Valid notifications produce no response item; an all-notification batch returns HTTP 204 with no body. Use unique IDs for unambiguous correlation. A batch does not pin all items to one upstream or create an atomic state snapshot. Specify block selectors where your application needs a particular state reference. Execution safety applies per item. `eth_sendRawTransaction` receives one upstream dispatch; placing it in a batch does not make replay safe. Subscription methods require WebSocket. See [Supported methods](/api/supported-methods). # Configured chains Source: https://docs.lasso.sh/api/chain-status List chains in the default public profile of RPC Core v0.5.0 ## Overview In Lasso RPC Core v0.5.0, this endpoint lists chains in the default `public` profile. A chain configured only in another file profile does not appear. The list does not test whether an upstream can currently serve the chain. ## Endpoint ``` GET /api/chains ``` ## Response Returns a JSON object containing the default profile's configured chains: Array of chain configuration objects The numeric chain ID as a string (e.g., "1" for Ethereum Mainnet) Human-readable name of the blockchain Internal identifier used in API paths and configuration Always `true` for a listed chain; this does not indicate upstream readiness ## Response Example ```json theme={null} { "chains": [ { "chain_id": "1", "name": "Ethereum Mainnet", "chain_name": "ethereum", "supported": true }, { "chain_id": "137", "name": "Polygon", "chain_name": "polygon", "supported": true }, { "chain_id": "42161", "name": "Arbitrum One", "chain_name": "arbitrum", "supported": true } ] } ``` ## Use Cases ### Discover Available Chains Query which chains the default `public` profile configures: ```bash theme={null} curl http://localhost:4000/api/chains ``` ### Validate Chain Configuration Check if a specific chain is configured in `public` before making a default-profile RPC request: ```bash theme={null} # Check if Ethereum is configured chains=$(curl -s http://localhost:4000/api/chains) has_ethereum=$(echo $chains | jq '.chains[] | select(.chain_name=="ethereum") | .chain_id') if [ -n "$has_ethereum" ]; then echo "Ethereum is configured with chain_id: $has_ethereum" fi ``` ### Dynamic Client Configuration Use this endpoint to build default-profile RPC URLs. It does not discover chains that exist only in custom profiles: ```javascript theme={null} const response = await fetch('http://localhost:4000/api/chains'); const { chains } = await response.json(); // Build RPC endpoints for each chain const rpcEndpoints = chains.reduce((acc, chain) => { acc[chain.chain_name] = `http://localhost:4000/rpc/${chain.chain_id}`; return acc; }, {}); console.log(rpcEndpoints); // { // ethereum: 'http://localhost:4000/rpc/1', // polygon: 'http://localhost:4000/rpc/137', // arbitrum: 'http://localhost:4000/rpc/42161' // } ``` ## Chain Name vs Chain ID Lasso RPC supports referencing chains by both their numeric `chain_id` and their `chain_name`: * **chain\_id**: Standard EVM chain identifier (e.g., `1`, `137`, `42161`) * **chain\_name**: Human-friendly identifier used in configuration files (e.g., `ethereum`, `polygon`, `arbitrum`) Both can be used in RPC endpoint paths: ```bash theme={null} # Using chain_id curl -X POST http://localhost:4000/rpc/1 \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' # Using chain_name (if supported by your configuration) curl -X POST http://localhost:4000/rpc/ethereum \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` ## Notes * The list comes from `public.yml`, not the union of all file profiles. See the released [chain controller](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso_web/controllers/chain_controller.ex) and [configuration store](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/config/config_store.ex). * All returned chains have `supported: true` because they are configured; this is not a live capability or availability check. * Validated profile YAML can be reloaded without restarting the instance; each node reads its own files. See [Profiles and reload](/concepts/profiles). * Use this endpoint for default-profile chain discovery; inspect a custom profile's configuration separately. # Connection lifecycle Source: https://docs.lasso.sh/api/connection-lifecycle WebSocket heartbeats, idle timeouts, reconnection, and subscription cleanup Lasso uses WebSocket ping/pong frames to detect disconnected clients. Keep your client responsive and reconnect when the connection closes. ## Connection establishment Use a standard WebSocket client: ```bash theme={null} wscat -c 'ws://localhost:4000/ws/rpc/ethereum' ``` The connection can close when you disconnect, fail to answer heartbeats, encounter a network interruption, or the server restarts. Subscription continuity exhaustion can also close the connection. Core does not authenticate the incoming client; protect this endpoint at your deployment boundary. ## Heartbeats | Setting | Value | Behavior | | - | - | - | | Initial heartbeat | 30 seconds | First ping after connecting | | Next heartbeat | 30 seconds | Scheduled after a pong or the first missed-pong timeout | | Pong deadline | 5 seconds | Time to answer each ping | | Missed-pong tolerance | 2 consecutive misses | Connection closes after the second deadline | Most WebSocket libraries, including browsers, automatically answer control-frame pings. You do not need to send JSON-RPC messages to keep the connection alive. Browser JavaScript does not expose ping/pong control frames. A pong resets the missed-heartbeat count. If a client never answers, the nominal timeline is: ```text theme={null} 0s Connection opens 30s Server sends ping #1 35s No pong: first missed heartbeat 65s Server sends ping #2 70s No pong: connection closes ``` That is approximately **40 seconds from the first unanswered ping**, or 70 seconds from a new connection that never responds. Scheduling and network delays can affect the observed times. Lasso answers client-initiated pings with pongs. Those pings do not substitute for answering the server's own pings. ## Idle timeout and session duration The WebSocket transport has a **two-hour idle timeout**. It is not a maximum session lifetime: responsive connections can remain open beyond two hours. Traffic, including heartbeat replies, prevents an idle connection from reaching that timeout. Reconnect whenever the connection closes. Do not rely on a periodic two-hour disconnect or a particular idle-timeout close message. ## Connection closure These application-level closure cases have explicit meanings: | Cause | Code | Meaning | | - | - | - | | Two missed heartbeat replies | 1002 | `Heartbeat timeout - no pong responses` | | Subscription continuity exhausted | 1011 | `Lasso subscription continuity exhausted` | A JSON-RPC rate-limit error can be returned on an open connection. It does not inherently mean the socket closes with code 1008. Deployments, access changes, proxies, and network failures can produce other close events; handle closure regardless of code. The released Core socket does not promise a dedicated slow-client close code. Bound your own processing and subscription volume, reconnect with capped backoff, and reconcile missed events after any closure. ## Reconnection Reconnect with capped exponential backoff and jitter, then recreate the subscriptions your application still needs. Subscription IDs belong to the old connection and must be replaced with the new IDs returned by `eth_subscribe`. This Node.js example uses the `ws` package and subscribes to new heads after each connection: ```javascript theme={null} import WebSocket from 'ws'; const endpoint = process.env.LASSO_WS_URL; if (!endpoint) throw new Error('Set LASSO_WS_URL to your WebSocket endpoint'); let attempts = 0; let stopped = false; let socket; let retryTimer; let stableTimer; function connect() { socket = new WebSocket(endpoint); socket.on('open', () => { // Reset backoff after the connection has remained stable. stableTimer = setTimeout(() => { attempts = 0; }, 30_000); socket.send(JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_subscribe', params: ['newHeads'] })); }); socket.on('message', (data) => { const message = JSON.parse(data.toString()); // Handle subscription acknowledgements, notifications, and RPC errors here. console.log(message); }); socket.on('error', () => { // The close event owns reconnect scheduling. }); socket.on('close', () => { clearTimeout(stableTimer); if (stopped) return; const ceiling = Math.min(30_000, 1_000 * 2 ** Math.min(attempts++, 5)); retryTimer = setTimeout(connect, ceiling * (0.5 + Math.random() * 0.5)); }); } function stop() { stopped = true; clearTimeout(retryTimer); clearTimeout(stableTimer); socket?.close(1000, 'Client shutdown'); } process.once('SIGINT', stop); process.once('SIGTERM', stop); connect(); ``` Recreating a subscription does not replay every event missed while your client was disconnected. Track your last processed block and use historical RPC reads to reconcile gaps where your application requires them. ## Subscription cleanup When your socket closes, Lasso detaches its logical subscriptions and releases their resources. Shared upstream streams can remain open for other clients or internal monitoring; disconnecting one client must not interrupt them. Use `eth_unsubscribe` when you no longer need an individual subscription. Closing the socket also cleans up its subscriptions; you do not need to wait for asynchronous unsubscribe calls during page unload. ## Operating guidance * Let your WebSocket library answer pings promptly; avoid blocking its event loop. * Treat a long gap in notifications separately from a failed connection. A healthy stream may have no matching events. * Track reconnect frequency, subscription errors, and the last processed block. * Expect reconnects during deployments and network changes. * If a profile or provider configuration changes, inspect the active routes before repeatedly retrying. # Error codes Source: https://docs.lasso.sh/api/error-codes Released RPC Core v0.5.0 JSON-RPC errors, routing exhaustion, and retry boundaries. This page describes **Lasso RPC Core v0.5.0**. Cloud has separate access, billing, and rate-limit behavior; see [Cloud RPC workload behavior](/cloud/rpc-behavior). Core preserves a valid, request-correlated upstream JSON-RPC error's `code`, `message`, and optional `data` when the upstream HTTP response is 2xx. An error's numeric code alone does not prove which provider failed or whether repeating the operation is safe. ## JSON-RPC envelope An error response has `jsonrpc`, `id`, and an `error` object with an integer `code` and string `message`. `data` is optional and its shape depends on the source of the error. A valid notification has no response; an all-notification batch returns HTTP 204. Core routes batch items independently. | Code | Meaning | Core example | | - | - | - | | `-32700` | Parse error | Malformed JSON. | | `-32600` | Invalid Request | An invalid request object or empty/oversized batch. | | `-32601` | Method not found or unsupported on this transport | `eth_subscribe` over HTTP. | | `-32602` | Invalid params | A method's parameters are invalid. | | `-32603` | Internal error | An error reported by Lasso or preserved from an upstream. | | `-32000` | Server error | Routing exhausted its eligible attempts. | These are categories, not a promise that every example uses exactly that code. For instance, a valid upstream error can carry its own server-defined code. Core does not issue Cloud API-key, account-quota, or strategy-entitlement errors; protect incoming Core traffic at your [deployment boundary](/api/authentication). ## HTTP subscriptions The released controller rejects `eth_subscribe` and `eth_unsubscribe` over HTTP with `-32601` and a WebSocket route hint. Connect through [WebSocket endpoints](/api/websocket-endpoints) for subscriptions. Core can forward provider-local HTTP filter methods when capability policy permits, with one dispatch and no cross-request affinity. Cloud rejects its [documented filter lifecycle](/cloud/rpc-behavior#stateful-http-filters) locally. ## Core routing exhaustion When no eligible attempt can serve a request, Core v0.5.0 returns a JSON-RPC `-32000` error over **HTTP 200**, preserving the request ID. `data` has `reason`, `retry_after_ms`, and `upstream_attempts`. `retry_after_ms` can be non-null only for `no_eligible_providers`. | `reason` | Meaning | Next action | | - | - | - | | `no_eligible_providers` | Configured routes are currently unavailable. | Honor a present `retry_after_ms` for a replay-safe request. | | `archive_required` | The request needs declared archival support. | Configure and qualify an archival provider. | | `transport_unavailable` | No eligible route supports the requested transport. | Configure that transport or change the request. | | `no_providers_configured` | The profile has no provider for the chain. | Correct the profile. | | `providers_excluded` | Every provider was explicitly excluded. | Review the override or exclusion. | | `routing_configuration_changed` | The route changed during selection. | Repeat a replay-safe request against the current configuration. | This serializer fixture uses a fixed retry interval; the live interval depends on current circuit state: ```json theme={null} { "error": { "code": -32000, "data": { "reason": "no_eligible_providers", "retry_after_ms": 5000, "upstream_attempts": 0 }, "message": "No available channels for method: eth_blockNumber. All circuits open, retry after 5s" }, "id": "client-request-42", "jsonrpc": "2.0" } ``` The shape comes from the released [controller regression](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/test/lasso_web/controllers/rpc_controller_test.exs) and [request pipeline](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/request/request_pipeline.ex). Request [metadata](/observability/request-metadata) can show recorded attempts when available. It does not promise an exhaustive provider diagnostic schema. ## Large `eth_getLogs` queries Core v0.5.0 turns a recognized provider block-range or result-size limit, or its own bounded response-size rejection, into one JSON-RPC error: ```json theme={null} { "jsonrpc": "2.0", "id": "client-request-42", "error": { "code": -32005, "message": "eth_getLogs result exceeds a range or size limit; reduce the block range", "data": { "reason": "log_range_too_large", "action": "reduce_block_range" } } } ``` Retry with a smaller `fromBlock`–`toBlock` span and collect the ranges in your application. Lasso does not automatically split the query or try another provider for this deterministic limit. A timeout without a recognized limit does not imply that reducing the range is the only remedy. Other malformed filter arguments retain their original error rather than this action. ## Upstream errors and retry safety An upstream can return a JSON-RPC error body with any HTTP status, but the released Core HTTP client validates and preserves that body only for a 2xx response. HTTP 429, 408, other 4xx, and 5xx statuses are classified as transport or upstream-status failures, even if their body contains a JSON-RPC error. A structurally valid, request-correlated error in a 2xx response keeps its provider code and wording. Gateway text, HTML, and malformed 2xx envelopes are not authoritative JSON-RPC errors. Core can fail over reviewed replay-safe reads within their dispatch budget and absolute deadline. Signed transaction submission receives one upstream dispatch; a lost response may follow acceptance. Reconcile its hash before deciding whether to rebroadcast. Stateful filters and unknown methods have different limits. See the [method-safety contract](/api/supported-methods) before adding application retries. ## WebSocket closure The released socket closes with code `1002` after two unanswered server heartbeats and `1011` when subscription continuity is exhausted. Other network, server, and proxy closures are possible. Reconnect, recreate the subscription, and use an application checkpoint to recover events missed while the client was disconnected. See [connection lifecycle](/api/connection-lifecycle). # Health Check Source: https://docs.lasso.sh/api/health-check Monitor Lasso RPC instance health and cluster status ## Overview In Lasso RPC Core v0.5.0, this endpoint reports application liveness, uptime, and cluster connectivity. It does not test whether an upstream can serve a request. ## Endpoint ``` GET /api/health ``` ## Response Returns a JSON object containing instance health information: Application liveness status ("healthy" when this endpoint responds) ISO 8601 timestamp of the health check Time in seconds since the Lasso RPC instance started Current version of Lasso RPC Cluster connectivity and status information Whether clustering is enabled for this instance Number of cluster nodes currently connected Number of cluster nodes actively responding to health checks Expected total number of nodes in the cluster List of geographic regions where cluster nodes are deployed Status derived from responding versus connected nodes: `standalone`, `healthy`, `degraded`, or `critical` ## Response Example ```json theme={null} { "status": "healthy", "timestamp": "2026-03-03T21:45:30.123456Z", "uptime_seconds": 86400, "version": "0.5.0", "cluster": { "enabled": true, "nodes_connected": 3, "nodes_responding": 3, "nodes_total": 3, "regions": ["us-east-1", "eu-west-1", "ap-southeast-1"], "status": "healthy" } } ``` ## Cluster Status Values The `cluster.status` field indicates the overall health of your cluster deployment: Clustering is not enabled. Instance is running independently. Every connected node is responding. This does not establish that every expected node is connected. Fewer nodes are responding than connected, but the responding count is at least `floor(nodes_connected / 2)`. The responding count is below `floor(nodes_connected / 2)`. The status calculation uses `nodes_connected`, not `nodes_total`. Monitor both counts to detect missing expected nodes; `cluster.status` alone does not prove full cluster coverage. This is the behavior of the released [health controller](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso_web/controllers/health_controller.ex) and [topology status function](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/cluster/health_topology.ex). ## Route readiness `GET /api/ready` checks whether the selected file profile has an eligible HTTP route and a fresh head observation for each selected chain. It defaults to the `public` profile and all its configured chains. Add `profile` and `chain` query parameters to check one route: ```bash theme={null} curl 'http://localhost:4000/api/ready?profile=public&chain=ethereum' ``` The endpoint returns HTTP `200` with `status: "ready"` when every selected chain is ready. It returns HTTP `503` with `status: "not_ready"` otherwise. Each item in `checks` includes `chain_id`, `status`, and a `reason`: either `no_eligible_upstream`, `stale_or_missing_head`, or `null` when ready. An unknown profile or chain returns `reason: "profile_or_chain_not_configured"`. Readiness uses local state. It does not call an upstream or prove a particular RPC method will succeed. Monitor an upstream-backed request separately. ## Use Cases ### Load Balancer Health Checks Configure your load balancer to poll this endpoint: ```bash theme={null} curl http://localhost:4000/api/health ``` ### Monitoring and Alerts Integrate with monitoring systems to track uptime and cluster status: ```bash theme={null} # Check if instance is healthy response=$(curl -s http://localhost:4000/api/health) status=$(echo $response | jq -r '.status') if [ "$status" != "healthy" ]; then echo "Alert: Lasso RPC is not healthy" fi ``` ### Cluster Health Monitoring Monitor cluster connectivity in distributed deployments: ```bash theme={null} # Alert if cluster is degraded cluster_status=$(curl -s http://localhost:4000/api/health | jq -r '.cluster.status') if [ "$cluster_status" = "degraded" ] || [ "$cluster_status" = "critical" ]; then echo "Alert: Cluster is $cluster_status" fi ``` ## Notes * This endpoint responds immediately and does not perform heavy computation * It does not make a live upstream RPC call; pair it with provider and routing monitoring * A healthy response confirms application availability, not that every upstream is usable * When clustering is disabled, `cluster.enabled` is `false` and `cluster.status` is `standalone` # HTTP Endpoints Source: https://docs.lasso.sh/api/http-endpoints Complete reference for Lasso RPC HTTP JSON-RPC endpoints ## Overview All HTTP RPC endpoints accept `POST` requests with `Content-Type: application/json` and follow the JSON-RPC 2.0 specification. ## Base Endpoint Routes requests using the default strategy (configurable, defaults to `load_balanced`). ``` POST /rpc/:chain ``` ### Parameters * `:chain` - Chain identifier (name or numeric ID) * **Chain name**: `ethereum`, `base`, `arbitrum` * **Chain ID**: `1`, `8453`, `42161` ### Example ```bash theme={null} curl -X POST http://localhost:4000/rpc/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` **Response:** ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": "0x8471c9a" } ``` ## Strategy Endpoints Explicitly specify the routing strategy for provider selection. ### Fastest ``` POST /rpc/fastest/:chain ``` Orders eligible providers by recent, reliability-qualified latency. Other eligible providers remain available for bounded fallback. ```bash theme={null} curl -X POST http://localhost:4000/rpc/fastest/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` ### Load Balanced ``` POST /rpc/load-balanced/:chain POST /rpc/round-robin/:chain ``` Starts from randomized order and gives distinct physical instances a first pass for replay-safe requests. ```bash theme={null} curl -X POST http://localhost:4000/rpc/load-balanced/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x0000000000000000000000000000000000000000","latest"],"id":1}' ``` ### Latency Weighted ``` POST /rpc/latency-weighted/:chain ``` Probabilistically routes requests with bias toward lower-latency providers. ```bash theme={null} curl -X POST http://localhost:4000/rpc/latency-weighted/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x0000000000000000000000000000000000000000","data":"0x"},"latest"],"id":1}' ``` ## Provider Override Endpoints Route directly to a specific provider, bypassing strategy selection. ``` POST /rpc/provider/:provider_id/:chain POST /rpc/:chain/:provider_id ``` ### Parameters * `:provider_id` - Provider identifier (e.g., `alchemy`, `infura`, `quicknode`) * `:chain` - Chain identifier ### Example ```bash theme={null} curl -X POST http://localhost:4000/rpc/provider/alchemy/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` Alternative syntax: ```bash theme={null} curl -X POST http://localhost:4000/rpc/ethereum/alchemy \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` ## Profile-Scoped Endpoints All routes above are available under a profile namespace for per-profile routing. ``` POST /rpc/profile/:profile/:chain POST /rpc/profile/:profile/fastest/:chain POST /rpc/profile/:profile/load-balanced/:chain POST /rpc/profile/:profile/round-robin/:chain POST /rpc/profile/:profile/latency-weighted/:chain POST /rpc/profile/:profile/provider/:provider_id/:chain POST /rpc/profile/:profile/:chain/:provider_id ``` ### Parameters * `:profile` - Profile slug (e.g., `public`, `testnet`, `production`) ### Example ```bash theme={null} curl -X POST http://localhost:4000/rpc/profile/testnet/fastest/base \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` Without an explicit profile in the URL, requests automatically use the `"public"` profile. Ensure `config/profiles/public.yml` exists at startup. A profile selects configuration; it does not authenticate clients or isolate tenant traffic. ## Method and transport policy Core forwards supported reads and can send `eth_sendRawTransaction` to one upstream. It rejects `eth_subscribe` and `eth_unsubscribe` over HTTP and provides a WebSocket route hint. Unknown methods and provider-local filter methods have separate dispatch and affinity limits. See the released [supported-methods contract](/api/supported-methods) for the complete method and execution-safety boundary. ## Request Headers ### Required Headers | Header | Value | Description | | - | - | - | | `Content-Type` | `application/json` | JSON request body | ### Optional Headers | Header | Example | Description | | - | - | - | | `X-Lasso-Provider` | `alchemy` | Override provider selection | | `X-Lasso-Transport` | `http` or `ws` | Force transport selection | | `X-Lasso-Include-Meta` | `headers` or `body` | Request observability metadata | ### Provider Override Header You can specify a provider via header instead of URL path: ```bash theme={null} curl -X POST http://localhost:4000/rpc/ethereum \ -H 'Content-Type: application/json' \ -H 'X-Lasso-Provider: alchemy' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` ## Query Parameters | Parameter | Values | Description | | - | - | - | | `include_meta` | `headers`, `body` | Return routing metadata | | `transport` | `http`, `ws` | Force transport selection | | `provider_override` or `provider_id` | provider ID | Override provider selection | Choose a strategy with its URL path, such as `/rpc/fastest/ethereum`. The default route does not use `?strategy=` to override its strategy. Core does not authenticate client requests; protect the endpoint at your [network or reverse proxy boundary](/api/authentication). ## Response Format ### Success Response ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": "0x8471c9a" } ``` ### Error Response For a request to `/rpc/not-a-chain` in the default `public` profile: ```json theme={null} { "jsonrpc": "2.0", "id": null, "error": { "code": -32602, "message": "Unsupported chain: Unknown chain 'not-a-chain' in profile 'public'" } } ``` ### Error Codes | Code | Meaning | | - | - | | `-32700` | Parse error (malformed JSON) | | `-32600` | Invalid Request (missing fields, batch too large) | | `-32601` | Method not found or not supported on this transport | | `-32602` | Invalid params (unsupported chain, missing chain\_id) | | `-32603` | Internal error | | `-32000` | Server error (for example, upstream rate limiting or exhausted routing) | ## Response Headers ### Standard Headers | Header | Description | | - | - | | `Content-Type` | `application/json` | | `X-Request-Id` | Phoenix request ID | ### Observability Headers With `include_meta=headers`: | Header | Description | | - | - | | `X-Lasso-Request-ID` | Lasso request tracking ID | | `X-Lasso-Meta` | Base64url-encoded routing metadata | ## CORS All origins are allowed (`*`). Allowed headers: * `Content-Type` * `Authorization` * `X-Requested-With` * `X-Lasso-Provider` * `X-Lasso-Transport` * `X-Lasso-Include-Meta` Preflight responses are cached for 24 hours. # Metrics API Source: https://docs.lasso.sh/api/metrics-api Retrieve default-profile performance metrics from RPC Core v0.5.0 ## Overview In Lasso RPC Core v0.5.0, the metrics API reports a chain in the default `public` profile, including provider statistics, RPC method performance, and VM resource usage. It does not accept a profile parameter or report a chain configured only in a custom profile. ## Endpoint ``` GET /api/metrics/:chain ``` ### Path Parameters A chain identifier in `public` (e.g., `ethereum`, `polygon`, `arbitrum`) ## Response The chain identifier The numeric chain ID (e.g., 1 for Ethereum) Unix timestamp in milliseconds when metrics were collected VM and system resource utilization Total memory usage in megabytes CPU utilization percentage Number of active Erlang processes Number of processes waiting to run Aggregated performance metrics for the chain Total number of RPC calls processed Success rate percentage (0-100) 50th percentile latency in milliseconds 95th percentile latency in milliseconds Reserved field; the released endpoint returns `null` Number of providers currently connected and healthy Total number of configured providers for this chain Reserved field; the released endpoint returns `null` Reserved field; the released endpoint returns `null` Error rate percentage (0-100) Performance metrics for each provider Provider identifier Human-readable provider name Relative provider ranking score; not a percentage or fixed 0-100 range Success fraction for this provider (0-1), rounded to two decimals Upstream attempts recorded for this provider Average response latency in milliseconds Number of calls in the last minute List of RPC methods that have been called on this chain Detailed RPC method performance organized by provider Provider identifier Provider name Performance data for each RPC method on this provider RPC method name (e.g., `eth_blockNumber`) Average latency in milliseconds 50th percentile latency 90th percentile latency 95th percentile latency 99th percentile latency Success rate for this method (0-1) Number of calls to this method Timestamp of last update Detailed RPC method performance organized by method RPC method name Performance data for each provider supporting this method (same structure as methods above, with added `provider_id` and `provider_name`) Timestamp when metrics were last updated ## Response Example ```json theme={null} { "chain": "ethereum", "chain_id": 1, "timestamp": 1709502330123, "system_metrics": { "memory_mb": 245.6, "cpu_percent": 12.4, "process_count": 542, "run_queue": 0 }, "chain_performance": { "total_calls": 15234, "success_rate": 99.2, "p50_latency": 125.5, "p95_latency": 342.1, "failovers_last_minute": null, "connected_providers": 3, "total_providers": 3, "recent_activity": null, "rpc_calls_per_second": null, "error_rate_percent": 0.8 }, "providers": [ { "id": "alchemy-eth-mainnet", "name": "Alchemy", "score": 3.51, "success_rate": 1.0, "total_calls": 8432, "avg_latency_ms": 115.3, "calls_last_minute": 28 }, { "id": "infura-eth-mainnet", "name": "Infura", "score": 3.16, "success_rate": 0.99, "total_calls": 4521, "avg_latency_ms": 145.8, "calls_last_minute": 12 }, { "id": "quicknode-eth-mainnet", "name": "QuickNode", "score": 2.96, "success_rate": 0.99, "total_calls": 2281, "avg_latency_ms": 128.2, "calls_last_minute": 5 } ], "rpc_methods": [ "eth_blockNumber", "eth_getBlockByNumber", "eth_call", "eth_getLogs" ], "rpc_performance_by_provider": [ { "provider_id": "alchemy-eth-mainnet", "provider_name": "Alchemy", "methods": [ { "method": "eth_blockNumber", "avg_latency_ms": 45.2, "p50_latency_ms": 42.0, "p90_latency_ms": 68.5, "p95_latency_ms": 85.3, "p99_latency_ms": 124.7, "success_rate": 0.998, "total_calls": 3421, "last_updated": 1709502330100 } ] } ], "rpc_performance_by_method": [ { "method": "eth_blockNumber", "providers": [ { "provider_id": "alchemy-eth-mainnet", "provider_name": "Alchemy", "avg_latency_ms": 45.2, "p50_latency_ms": 42.0, "p90_latency_ms": 68.5, "p95_latency_ms": 85.3, "p99_latency_ms": 124.7, "success_rate": 0.998, "total_calls": 3421, "last_updated": 1709502330100 } ] } ], "last_updated": 1709502330100 } ``` ## Error Responses ### Chain Not Found ```json theme={null} { "error": "Chain not found", "chain": "invalid-chain", "available_chains": ["ethereum", "polygon", "arbitrum"] } ``` **HTTP Status**: `404 Not Found` This includes chains configured only in another profile. The response's `available_chains` lists canonical chain names in `public`; see the released [metrics controller](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso_web/controllers/metrics_controller.ex). ## Use Cases ### Monitor Chain Performance Track overall performance metrics for a blockchain: ```bash theme={null} curl http://localhost:4000/api/metrics/ethereum ``` ### Provider Performance Comparison Compare performance across different RPC providers: ```bash theme={null} # Get provider rankings curl -s http://localhost:4000/api/metrics/ethereum | \ jq '.providers | sort_by(-.score) | .[] | {name, score, success_rate, avg_latency_ms}' ``` ### RPC Method Analysis Analyze performance of specific RPC methods: ```bash theme={null} # Check eth_getLogs performance across providers curl -s http://localhost:4000/api/metrics/ethereum | \ jq '.rpc_performance_by_method[] | select(.method=="eth_getLogs")' ``` ### System Resource Monitoring Monitor system resource usage: ```bash theme={null} # Alert if memory usage is high memory_mb=$(curl -s http://localhost:4000/api/metrics/ethereum | jq '.system_metrics.memory_mb') if (( $(echo "$memory_mb > 1000" | bc -l) )); then echo "Warning: High memory usage: ${memory_mb}MB" fi ``` ### Performance Dashboards Transform the JSON values for your monitoring backend. For native Prometheus text, scrape `GET /metrics` on each node; see [metrics](/observability/metrics). The JSON endpoint remains useful when you need the chain and provider fields: ```javascript theme={null} // Fetch metrics periodically setInterval(async () => { const response = await fetch('http://localhost:4000/api/metrics/ethereum'); const metrics = await response.json(); // Send to monitoring backend await sendToDatadog({ 'lasso.chain.success_rate': metrics.chain_performance.success_rate, 'lasso.chain.p95_latency': metrics.chain_performance.p95_latency, 'lasso.chain.total_calls': metrics.chain_performance.total_calls, 'lasso.system.memory_mb': metrics.system_metrics.memory_mb, 'lasso.system.cpu_percent': metrics.system_metrics.cpu_percent }); }, 30000); // Every 30 seconds ``` `rpc_calls_per_second`, `failovers_last_minute`, and `recent_activity` are reserved fields that return `null` in v0.5.0. Derive a rate from successive `total_calls` samples if your monitoring system needs one. ## Notes * Available values reflect retained in-process upstream-attempt observations. * Performance percentiles (p50, p95, etc.) are calculated over a sliding window * Provider scores combine the success fraction, average latency, and observation count. They rank providers within the retained data; they are not percentages. * The `rpc_performance_by_provider` and `rpc_performance_by_method` fields provide the same data organized differently for convenience * System metrics reflect the Erlang VM's resource usage, not the underlying host system # Response formats Source: https://docs.lasso.sh/api/response-formats JSON-RPC envelopes and released Core v0.5.0 metadata **Lasso RPC Core v0.5.0** returns JSON-RPC responses. Metadata is optional. [Cloud workload behavior](/cloud/rpc-behavior) defines the separate managed-service execution and compatibility boundaries. ## Standard responses Successful requests contain `jsonrpc`, the echoed JSON-RPC `id`, and a method-specific `result`: ```json theme={null} {"jsonrpc":"2.0","id":1,"result":"0x64"} ``` An error response contains `error` instead of `result`. The error has a numeric `code`, a `message`, and optional `data`. Do not assume an upstream HTTP failure becomes the same downstream HTTP status or exposes internal provider attempts. Routing exhaustion is a JSON-RPC error over HTTP 200; malformed requests and admission failures have their own responses. See [error codes](/api/error-codes) and [supported methods](/api/supported-methods). Valid request IDs retain their type and value. Notifications omit an ID and have no JSON-RPC response. Batch behavior and transport differences are documented in the [method contract](/api/supported-methods); a batch is not one atomic read. ## Observability metadata The released schema uses `chain_id`, `end_to_end_latency_ms`, `selection_latency_ms`, `lasso_overhead_ms`, `executed_channel` and `attempted_channels`. It does not promise a `timestamp`, `total_latency_ms`, or provider display-name/region field. Top-level nil values are omitted. Use the single [metadata field reference](/observability/request-metadata#metadata-fields) for optional fields, channel identity, opaque correlation IDs and head-policy extensions. These fields describe Core v0.5.0, not a Cloud quota or billing schema. ### Body mode Add `include_meta=body`, and the response carries a top-level `lasso_meta` object: ```bash theme={null} curl 'http://localhost:4000/rpc/ethereum?include_meta=body' \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` The [request metadata](/observability/request-metadata#body-mode) page shows generated success and failover records. Read the raw response envelope; an SDK's method result does not necessarily retain `lasso_meta`. ### Headers mode Use `include_meta=headers` or `X-Lasso-Include-Meta: headers`. The response carries an opaque `X-Lasso-Request-ID` and, within the size limit, a base64url `X-Lasso-Meta` JSON object with the same schema as body mode. See [decoding and size limits](/observability/request-metadata#headers-mode). ## WebSocket responses Forwarded calls use JSON-RPC response envelopes. Subscription events use the `eth_subscription` notification with `params.subscription` and `params.result`: ```json theme={null} { "jsonrpc": "2.0", "method": "eth_subscription", "params": { "subscription": "subscription-id", "result": {"number": "0x64"} } } ``` The event above shows only the envelope and one illustrative result field; actual payloads depend on the subscription type. Subscription IDs are opaque. See [subscriptions](/api/subscriptions) and the [recovery guide](/advanced/websocket-subscriptions) for supported streams and limits. # Subscriptions Source: https://docs.lasso.sh/api/subscriptions Real-time blockchain event subscriptions via WebSocket Lasso supports Ethereum JSON-RPC subscriptions over WebSocket for real-time blockchain events. Use `eth_subscribe` to create subscriptions and `eth_unsubscribe` to cancel them. ## Subscription Types Lasso supports the following subscription types: | Type | Description | | - | - | | `newHeads` | New block headers as they arrive | | `logs` | Log events matching a filter | ## Creating Subscriptions ### Subscribe to New Block Headers **Request:** ```json theme={null} {"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1} ``` **Response:** ```json theme={null} {"jsonrpc":"2.0","id":1,"result":"0xabc123..."} ``` The `result` field contains the subscription ID. Save this ID to unsubscribe later. **Subscription Events:** As new blocks arrive, the server pushes events: ```json theme={null} { "jsonrpc":"2.0", "method":"eth_subscription", "params":{ "subscription":"0xabc123...", "result":{ "parentHash":"0x...", "sha3Uncles":"0x...", "miner":"0x...", "stateRoot":"0x...", "transactionsRoot":"0x...", "receiptsRoot":"0x...", "logsBloom":"0x...", "difficulty":"0x0", "number":"0x8471c9a", "gasLimit":"0x1c9c380", "gasUsed":"0x5208", "timestamp":"0x65f3a2b0", "extraData":"0x...", "mixHash":"0x...", "nonce":"0x0000000000000000", "baseFeePerGas":"0x7", "hash":"0x..." } } } ``` ### Subscribe to Logs **Request with filter:** ```json theme={null} { "jsonrpc":"2.0", "method":"eth_subscribe", "params":[ "logs", { "address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"] } ], "id":2 } ``` This example subscribes to Transfer events from USDC on Ethereum. **Response:** ```json theme={null} {"jsonrpc":"2.0","id":2,"result":"0xdef456..."} ``` **Subscription Events:** As matching logs are emitted, the server pushes events: ```json theme={null} { "jsonrpc":"2.0", "method":"eth_subscription", "params":{ "subscription":"0xdef456...", "result":{ "address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "topics":[ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000742d35cc6634c0532925a3b844bc454e4438f44e", "0x0000000000000000000000001e0049783f008a0085193e00003d00cd54003c71" ], "data":"0x0000000000000000000000000000000000000000000000000000000002faf080", "blockNumber":"0x8471c9a", "transactionHash":"0x...", "transactionIndex":"0x0", "blockHash":"0x...", "logIndex":"0x0", "removed":false } } } ``` ### Log Filter Options The `logs` subscription accepts a filter object with: | Field | Type | Description | | - | - | - | | `address` | string or array | Contract address(es) to filter. Omit for all contracts. | | `topics` | array | Event signature and indexed parameters. Use `null` for wildcards. | **Examples:** **All events from a contract:** ```json theme={null} { "address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } ``` **Specific event signature across all contracts:** ```json theme={null} { "topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"] } ``` **Event from multiple contracts:** ```json theme={null} { "address":[ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "0xdac17f958d2ee523a2206206994597c13d831ec7" ], "topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"] } ``` **Event with indexed parameter filter:** ```json theme={null} { "address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "topics":[ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", null, "0x0000000000000000000000001e0049783f008a0085193e00003d00cd54003c71" ] } ``` This filters for Transfer events where the second indexed parameter (recipient) matches the specified address. ## Unsubscribing Cancel a subscription using `eth_unsubscribe`: **Request:** ```json theme={null} {"jsonrpc":"2.0","method":"eth_unsubscribe","params":["0xabc123..."],"id":3} ``` **Response:** ```json theme={null} {"jsonrpc":"2.0","id":3,"result":true} ``` A result of `true` indicates successful unsubscription. After unsubscribing, no more events will be pushed for that subscription ID. ## Full Example Here's a complete subscription flow using `wscat`: ```bash theme={null} # Connect to WebSocket endpoint wscat -c 'ws://localhost:4000/ws/rpc/ethereum' # Subscribe to new blocks > {"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1} < {"jsonrpc":"2.0","id":1,"result":"0xabc123..."} # Receive block events < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0xabc123...","result":{...}}} < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0xabc123...","result":{...}}} # Unsubscribe > {"jsonrpc":"2.0","method":"eth_unsubscribe","params":["0xabc123..."],"id":2} < {"jsonrpc":"2.0","id":2,"result":true} ``` ## Subscription Lifecycle ### Automatic Cleanup Subscriptions are automatically cleaned up when: * The WebSocket connection closes * The connection times out (see [Connection Lifecycle](/api/connection-lifecycle)) * An `eth_unsubscribe` request is received ### Multiple Subscriptions You can create multiple subscriptions on a single WebSocket connection: ```json theme={null} // Subscribe to blocks {"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1} // Subscribe to logs {"jsonrpc":"2.0","method":"eth_subscribe","params":["logs",{"address":"0x..."}],"id":2} ``` Each subscription receives a unique subscription ID and events are tagged with the corresponding ID. ## Error Handling ### Invalid Subscription Type **Request:** ```json theme={null} {"jsonrpc":"2.0","method":"eth_subscribe","params":["invalidType"],"id":1} ``` **Response:** ```json theme={null} { "jsonrpc":"2.0", "id":1, "error":{ "code":-32602, "message":"Invalid subscription parameters" } } ``` For `logs`, Core accepts a filter object and forwards it to the selected upstream. An unsupported field is not rejected locally with a fixed “malformed filter” response; upstream validation and error details can vary. ### Unsubscribe Non-Existent Subscription **Request:** ```json theme={null} {"jsonrpc":"2.0","method":"eth_unsubscribe","params":["0xnonexistent"],"id":3} ``` **Response:** ```json theme={null} {"jsonrpc":"2.0","id":3,"result":false} ``` A result of `false` indicates the subscription ID was not found. ## Best Practices ### Track Subscription IDs Always store subscription IDs returned by `eth_subscribe` to unsubscribe later: ```javascript theme={null} const subscriptionIds = new Map(); // Subscribe const request = {jsonrpc: "2.0", method: "eth_subscribe", params: ["newHeads"], id: 1}; ws.send(JSON.stringify(request)); ws.on('message', (data) => { const response = JSON.parse(data); if (response.id === 1) { subscriptionIds.set('newHeads', response.result); } }); // Unsubscribe function cleanup() { const subId = subscriptionIds.get('newHeads'); if (subId) { const request = {jsonrpc: "2.0", method: "eth_unsubscribe", params: [subId], id: 2}; ws.send(JSON.stringify(request)); } } ``` ### Handle Reconnection Subscriptions don't persist across reconnections. Re-subscribe after reconnecting: ```javascript theme={null} ws.on('open', () => { // Re-subscribe to all active subscriptions subscribeToNewHeads(); subscribeToLogs(); }); ``` ### Filter Logs Efficiently Use specific filters to reduce network traffic: ```json theme={null} // Good: Specific contract and event { "address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"] } // Avoid: No filter (receives all logs) {} ``` ### Rate Limiting Lasso RPC Core has no built-in incoming customer quota mechanism. Enforce ingress connection/request limits at your proxy and account for upstream-provider limits and subscription volume. Use bounded filters and follow the [recovery and checkpoint guide](/advanced/websocket-subscriptions). [Cloud limits](/cloud/rpc-behavior) are a separate managed-service contract. # Supported methods Source: https://docs.lasso.sh/api/supported-methods The released RPC Core method, transaction, subscription, and affinity contract. This page describes the self-hosted **v0.5.0** runtime. Lasso Cloud has a separate [compatibility contract](https://docs.lasso.sh/cloud/json-rpc-compatibility). Do not infer parity from a shared method name or product version. ## Reads and execution safety Common Ethereum reads such as `eth_blockNumber`, `eth_getBalance`, `eth_call`, `eth_getTransactionReceipt`, and `eth_getLogs` are routable, subject to provider capability, transport availability, history, and request parameters. A routable method is not a guarantee that every provider supports it. Methods classified as replay-safe by the released `ExecutionEnvelope` can use bounded further attempts within the original deadline. Unknown methods receive one upstream dispatch. Provider error classification does not override execution safety. The registry classifies methods; it does not certify upstream semantics or full compliance with every Ethereum specification. An HTTP endpoint is rejected after a malformed or mismatched `eth_chainId` probe. A later matching probe can restore eligibility; an ordinary successful request cannot clear the rejection. Endpoints with no identity evidence remain subject to normal admission. This check does not establish WebSocket identity or attest the contents of arbitrary RPC responses. ## Signed transaction submission `eth_sendRawTransaction` receives one upstream dispatch. Lasso does not retry or fan out after dispatch. A lost response may mean the upstream accepted the transaction. Clients remain responsible for signing, nonce management, replacement, transaction-hash reconciliation, receipts, and finality. ## Node-local methods The transport policy globally disallows `eth_sendTransaction`, `eth_accounts`, `eth_sign`, `eth_signTransaction`, and `personal_sign`. Provider capability policy can reject additional methods, including the `local_only` category. Do not treat a profile as an authentication or authorization boundary. ## Stateful filters and extensions RPC Core v0.5.0 can forward provider-local filter methods when capability policy permits them. They receive one dispatch per request and have **no cross-request affinity guarantee**. A filter ID created on one upstream may be invalid on a later selected upstream. Prefer `eth_getLogs` or supported WebSocket subscriptions; Core does not advertise a reliable multi-provider filter lifecycle. Debug, trace, transaction-pool, bundler, and chain-specific methods depend on upstream support and configured restrictions. Their presence in a registry does not provide sticky sessions, common history, or a universal parameter contract. ## WebSocket subscriptions The supported stream kinds are `newHeads` and `logs`. Use: ```json theme={null} {"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1} ``` ```json theme={null} {"jsonrpc":"2.0","method":"eth_subscribe","params":["logs",{}],"id":2} ``` Use `eth_unsubscribe` with the returned subscription ID to stop the stream. Pending-transaction subscriptions are not part of this released subset. HTTP subscription requests return a method error with a WebSocket URL hint. Matching client streams can share upstream subscriptions. Establishment requires a usable upstream before returning a subscription ID. Recovery uses bounded HTTP replay and a replacement live stream. Recovery beyond its time, replay, attempt, or buffer limits terminates the affected downstream connection explicitly; it does not promise unbounded or lossless delivery through arbitrary outages. In Core v0.5.0, `subscribe_new_heads` controls automatic block monitoring **and** client `newHeads` eligibility. Providers inherit the chain setting, which defaults to `true`; override it per provider when needed. New profiles can reuse an already-connected upstream after a successful configuration reload. See [Configuration](/configuration/overview) and [Deployment](/deployment/docker). ## HTTP batches HTTP endpoints accept arrays with up to 50 items by default (`max_batch_requests`). Items are validated and routed independently with bounded concurrency. A batch is not an atomic state snapshot and does not pin every item to one provider. Response items retain request order. Valid notifications produce no response; an all-notification batch returns HTTP 204. Use unique IDs for correlation. ## Deployment boundary Core does not authenticate clients or enforce incoming customer quotas. Protect RPC, metrics, and dashboard endpoints through your network or reverse proxy. Profile tester settings do not create API quotas or upstream quota isolation. The authoritative implementation is `TransportPolicy`, `ExecutionEnvelope`, `Capabilities`, `RPCController`, and `RPCSocket.ItemOwner` at the selected release. See [API reference](/api/http-endpoints) for routes and metadata. # WebSocket endpoints Source: https://docs.lasso.sh/api/websocket-endpoints Unary RPC and bounded subscriptions in RPC Core v0.5.0. Connect to `ws://localhost:4000/ws/rpc/ethereum` for the included public profile. For a named profile, use `/ws/rpc/profile/my-app/ethereum`. Explicit strategy routes include `/ws/rpc/fastest/ethereum`, `/ws/rpc/load-balanced/ethereum`, and `/ws/rpc/latency-weighted/ethereum`. ```bash theme={null} npx --yes wscat -c ws://localhost:4000/ws/rpc/ethereum ``` Send an ordinary request: ```json theme={null} {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1} ``` Subscribe to blocks: ```json theme={null} {"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":2} ``` A successful establishment returns a subscription ID. Check that subsequent `eth_subscription` notifications contain real block headers; an open socket alone does not prove subscription delivery. To stop a subscription, send `eth_unsubscribe` with the returned ID as its only parameter. Subscription ownership belongs to the downstream connection. ## Provider configuration A connected upstream WebSocket is required. In RPC Core v0.5.0, `subscribe_new_heads: true` enables both automatic block tracking and client `newHeads` eligibility. New profiles sharing an existing connection work after successful reload. See [Provider configuration](/configuration/providers). ## Supported streams and recovery The released subset includes `newHeads` and `logs`, not pending transactions. Recovery after a provider failure is bounded and can terminate the connection explicitly; [WebSocket subscriptions](/advanced/websocket-subscriptions) defines it and what your application must checkpoint. WebSocket batching is not part of the advertised interface. Ordinary RPC execution still follows the [method safety contract](/api/supported-methods). For a public deployment, terminate TLS to provide `wss://` and protect the endpoint through your network or reverse proxy. Core does not authenticate clients. # Choose an adoption path Source: https://docs.lasso.sh/cloud/adoption Decide between managed routing, custom providers, and a staged RPC rollout # Choose an adoption path Lasso can sit in front of a managed provider pool, an existing set of provider accounts, or a mixture of public, commercial, and self-hosted infrastructure. Choose the smallest first step that tests the property you care about. ## Managed profile Use a managed profile when you want to evaluate Lasso without moving upstream credentials. Replace one RPC URL in a non-critical workload, choose an explicit routing strategy, and compare correctness, latency, and failure behavior with the current endpoint. This path answers: “Does Lasso improve this request path?” It does not answer whether your existing provider contracts should become part of the pool. ## Custom profile Use a custom profile when you already pay for Alchemy, QuickNode, Infura, run your own nodes, or depend on a specialized provider. A custom profile stores those upstream endpoints in Lasso and makes the resulting pool addressable by a profile-specific route. A mixed pool can improve different properties: * **failure independence:** avoid a single upstream account, deployment, or provider becoming the only path; * **quota headroom:** move attempts away from an upstream that is throttling; * **latency:** let regional measurements prefer different providers for different methods; * **coverage:** route a method or historical query only to providers whose declared capabilities satisfy it; * **cost control:** use contracts you already own while keeping an alternate route available. These benefits depend on real independence and capability overlap. Two URLs backed by the same account, region, or infrastructure may not remove the failure mode you care about. ## Stage the rollout 1. Inventory chains, critical methods, HTTP and WebSocket use, current providers, request volume, and correctness requirements. 2. Select one stateless read path with an observable success criterion. 3. Make the strategy explicit in the URL during evaluation. 4. Compare request results and routing evidence; do not infer redundancy from provider count alone. 5. Add historical, write, filter, and subscription workloads only after reading their specific boundaries in [RPC workload behavior](/cloud/rpc-behavior). 6. Expand traffic gradually and retain the previous endpoint as an application rollback until the integration is accepted. Before adding archive traffic, use [historical workload qualification](/cloud/historical-workloads) to test your exact methods, depths and selectors through the intended profile. ## What to measure * successful response rate and error classification; * latency by method and region, not only a global average; * which provider served a request and how many attempts were required; * behavior during upstream throttling and failure; * method, archive, log-range, and WebSocket coverage; * quota or spend in each upstream provider's own dashboard. The dashboard is an operational view, not a durable request ledger. Use request IDs and your existing telemetry when the evaluation needs incident-grade correlation. # Operate RPC with an agent Source: https://docs.lasso.sh/cloud/agent-flows Create an endpoint, buy prepaid credit, hand it to its owner, and manage custom profiles over HTTP. An agent can run the full endpoint lifecycle over Lasso's HTTP management API with its existing HTTP tools. No SDK or CLI is required. The operational reference for agents is [`/SKILL.md`](https://lasso.sh/SKILL.md). Check live availability before following a recipe: `payments.prepaid` and `auth.claiming` in [`/agent.json`](https://lasso.sh/agent.json), and operation flags in the [configuration schema](https://lasso.sh/api/v1/management/configuration-schema). [`/openapi.json`](https://lasso.sh/openapi.json) has the exact request and response schemas. Three credentials stay separate: the **RPC key** goes in the application's secret store, the **management token or delegated credential** stays with the agent, and **wallet authority** stays with the user's signer. None substitutes for another. Key creation delivers its secret once. If the response is lost, create another key; the first secret cannot be recovered. A Custom profile `PUT` is the whole desired document, so repeat it with the same body after a lost response. Use `If-Match` with the revision you read to protect concurrent edits. For a credit or Custom-access purchase, retry only the same signed payment and check its returned `status_url` if settlement is pending. ## Create a managed endpoint Check `auth.keys.creation_enabled` in `/agent.json`, then create a key without signup or a credential: ```http theme={null} POST /api/v1/management/keys Content-Type: application/json {"name":"trading-bot"} ``` Save the returned `id`, `key`, `rpc_url` and `management_token` privately. The token manages every key it creates until the owner claims them. It does not authorize a payment. Append a chain to the returned RPC URL: ```text theme={null} https://lasso.sh/rpc/k//load-balanced/base ``` `GET /api/v1/management/keys/` with the management token as Bearer inspects balance and recent usage without returning the RPC secret. ## Top up with prepaid credit Prepaid credit buys premium RPC usage on the same key and URL. Rails are USDC.e on Tempo (`mpp`) and USDC on Base (`x402`). Use only a rail with `enabled: true` in `/agent.json`, and check its live `minimum_usd` and `maximum_usd`. Anyone can pay for a key; Lasso credentials are not wallet authority. Stay within the user's approved budget. ```http theme={null} POST /api/v1/management/keys//credit Content-Type: application/json {"usd":"1"} ``` The first response is HTTP `402`. Read `PAYMENT-REQUIRED` for x402 or `WWW-Authenticate` for MPP. Check the amount, network, asset, and recipient before asking the wallet to sign. The challenge does not move money. Repeat the same `/credit` request and body with one signed payment: | Rail | Wallet credential | | - | - | | `x402` | `PAYMENT-SIGNATURE` | | `mpp` | `Authorization: Payment ...` | A standard x402 or MPP client can handle the challenge and retry. Do not send the management token as the wallet credential. HTTP `200` returns credited balance and a receipt. HTTP `202` means the transfer is still settling; use the returned `status_url` to check the same purchase. Never sign a second payment while one is pending. Repeating the same signed payment returns its result without crediting twice. For account-owned Custom access, use the same exchange at `POST /api/v1/management/keys//custom-access` with `{"days":7}`. The key must be claimed first. See the live daily price in `/agent.json`. ## Hand the app to its owner ```http theme={null} POST /api/v1/management/keys//claim-link Authorization: Bearer ``` Give the owner the returned `claim_url` privately; it expires in 10 minutes. Claiming keeps the key and URL, moves every key created by the same token and their remaining credit to the account, and ends anonymous management. The owner grants later management or funding separately in **Agent access**. A claimed app can move to an active custom profile with `PATCH /api/v1/management/keys/` and `{"profile":""}`, keeping its secret and URL. Use an owner-approved management credential with access to that profile. ## Manage existing providers An agent asks the owner to connect it to the account: ```http theme={null} POST /api/v1/management/connections Content-Type: application/json {"name":"Set up RPC for my app"} ``` Store the one-time `token` privately and send the owner only `approval_url`. Poll `GET /api/v1/management/connections/` with that token as Bearer auth no faster than every five seconds. The owner has 15 minutes to approve. Once approved, the same token manages ordinary RPC resources across the account; `GET /api/v1/management/me` shows its authority. Approval does not authorize wallet payment. The owner may narrow the credential through the signed-in owner API or revoke it in **Agent access**. Check `auth.profiles.documents_enabled` in `/agent.json` and fetch `GET /api/v1/management/configuration-schema` for the accepted document and recipes. Read a profile with `GET /api/v1/management/profiles/`; provider URLs are never returned. Create or replace it with a whole-document `PUT`: ```http theme={null} PUT /api/v1/management/profiles/my-app?dry_run=true Authorization: Bearer Content-Type: application/json {"chains":{"base":{"providers":[ {"name":"primary","url":""}, {"name":"backup","url":""}]}}} ``` Start with the URLs and omit optional provider `limits` unless the owner has a known policy to enforce. Test historical reads through the saved Lasso route and inspect the selected provider before adding an explicit override. A dry run checks chain identity and reachability for new or changed providers. It does not store the document, require Custom access, or prove archive coverage. Repeat the `PUT` without `dry_run` to save it. A wrong-chain provider rejects the whole document; an unreachable provider can remain `verifying` until a later check succeeds. Send `If-Match: ` when changing a profile you already read. Omitted providers are removed; omitting a retained provider's `url` keeps its stored secret. Saving a profile requires Custom access, which the owner can subscribe to or purchase for a claimed key through the separate 402 exchange. ## Integrate and evaluate Change one endpoint using the codebase's existing secret manager, keep a rollback, and verify a real application query before moving more traffic. The [Base viem and ethers guide](/cloud/base-rpc-integration) shows a client integration. Test historical reads, transaction sending and subscriptions against their [workload behavior](/cloud/rpc-behavior). # Use Lasso with viem or ethers on Base Source: https://docs.lasso.sh/cloud/base-rpc-integration Replace one Base RPC URL, verify chain identity, and evaluate Lasso without changing your Ethereum client. Lasso exposes standard EVM JSON-RPC. Start with one server-side read path in your Base application and replace its RPC URL with a Lasso endpoint. Keep your client library and application contract calls. ## Prepare the endpoint Check the `chains` list in [live service discovery](https://lasso.sh/agent.json) for Base before choosing a managed profile. If you have existing Base provider URLs, configure a [custom provider pool](/cloud/bring-your-own-rpc) instead. Use the [quickstart](/cloud/quickstart) or the available [agent workflow](/cloud/agent-flows) to obtain an endpoint. Store the complete endpoint as `LASSO_RPC_URL` in your existing server-side secret manager. Keep the previous RPC URL for rollback. An endpoint containing a key is a credential; exposing it in a browser bundle lets visitors use its access and quota. ## viem ```typescript theme={null} import { createPublicClient, http } from "viem"; import { base } from "viem/chains"; const url = process.env.LASSO_RPC_URL; if (!url) throw new Error("LASSO_RPC_URL is required"); const client = createPublicClient({ chain: base, transport: http(url), }); if ((await client.getChainId()) !== base.id) { throw new Error("Unexpected RPC chain"); } const blockNumber = await client.getBlockNumber(); console.log({ blockNumber: blockNumber.toString() }); ``` See the [viem public client reference](https://viem.sh/docs/clients/public) for client options. Configure application timeouts and retries around the operations you actually perform; transaction submission has different recovery needs from a read. ## ethers v6 ```typescript theme={null} import { JsonRpcProvider } from "ethers"; const url = process.env.LASSO_RPC_URL; if (!url) throw new Error("LASSO_RPC_URL is required"); const provider = new JsonRpcProvider(url, undefined, { batchMaxCount: 1, polling: true, }); try { if ((await provider.getNetwork()).chainId !== 8453n) { throw new Error("Unexpected RPC chain"); } console.log({ blockNumber: await provider.getBlockNumber() }); } finally { provider.destroy(); } ``` This evaluation sends individual requests and uses polling rather than provider-local HTTP filters. Lasso rejects those filters. Once the flow works, choose batching within the live Lasso and upstream limits. The [ethers JSON-RPC provider reference](https://docs.ethers.org/v6/api/providers/jsonrpc/) describes these options. ## Verify the application flow Run a representative read with the same block selector against both endpoints. For related state values, [select one block hash](/advanced/read-at-one-block) so head movement does not masquerade as a routing discrepancy. Record latency, error rate and [routing evidence](/cloud/observability) without logging credentials. Move one request flow first. If its behavior fails your acceptance criteria, restore the previous URL. Test transaction sending, historical ranges and WebSocket subscriptions separately; an `eth_blockNumber` response does not qualify those workloads. # Use your existing RPC providers with Lasso Cloud Source: https://docs.lasso.sh/cloud/bring-your-own-rpc Put the EVM provider accounts and nodes you already use behind one Lasso Cloud URL, then verify routing and upstream behavior. **Lasso Cloud · For teams with existing provider accounts or nodes.** Use a custom profile to put those EVM upstreams behind one application RPC URL. Your application keeps a standard JSON-RPC client. Lasso Cloud manages the provider pool behind its endpoint, so changing an upstream does not require an application redeployment. ## Start with one request flow Choose one chain and a stateless read your app already uses. Keep the existing endpoint available for rollback. Record its latency, errors and request volume before changing the route. Two providers can improve your options during an outage, but they may share infrastructure or disagree about the chain head. Custom access and upstream provider charges are separate. Check [live pricing](https://lasso.sh/pricing) and your provider quotas before the trial. ## Configure the pool 1. Create a custom profile in the [dashboard](https://lasso.sh/dashboard). 2. Add the chosen chain and the two HTTP URLs from your existing providers. 3. Check that each URL serves the intended chain and methods. Start with `eth_chainId`, `eth_blockNumber` and the exact read your app needs. 4. Choose your block policy using the guidance below, then activate the profile. 5. Send the same read through the profile endpoint and inspect its [routing evidence](/cloud/observability). Keep provider credentials in the configuration secret store. Do not put them in support screenshots, chat transcripts or application logs. Check the [supported connection settings](/cloud/custom-profiles#supported-provider-connection-settings) if a provider needs header authentication or a distinct WebSocket endpoint. Dashboard capability probes and management API probes have different scopes. The management probe, when advertised by the live API, checks chain identity and head within the call budget in its [live configuration schema](https://lasso.sh/api/v1/management/configuration-schema). It does not establish archive, WebSocket or method coverage. Test those requirements separately. ## Choose behavior for your workload | Requirement | Starting point | What to verify | | - | - | - | | Responsive current reads | `fastest` strategy with block regression protection off | Tail latency, errors and head freshness under your traffic | | Sequential latest-block observations | Enable block regression protection for the chain | Lag behavior and how your app handles a continuity error | | Several values from one state | Select a block hash and reuse it for all reads | Upstream support for the block-hash selector | | Real-time logs | Account-backed WebSocket endpoint | Reconnect, duplicate handling and bounded replay recovery | [Block regression protection](/advanced/block-continuity) does not make independent `latest` contract calls a snapshot. Use [Read at one block](/advanced/read-at-one-block) for related balances, nonces or contract values. Neither feature guarantees finality. ## Update without changing the app endpoint Start by adding or adjusting one provider. Verify its behavior, apply the profile change, then inspect routing evidence from the application. Cluster publication is not instantaneous; a successful local reconciliation does not prove every region has observed the change. For agent-managed changes, follow the [management workflow](/cloud/agent-flows). Read the current revision, dry-run the complete desired profile document, then apply the same document with `If-Match: `. Repeating the same `PUT` after a lost response is safe; re-read before changing an already updated profile. Omitting a provider removes it; omitting an existing provider's URL preserves that secret. If a provider performs poorly, remove or correct it while other eligible providers remain available. Keep an application rollback for the evaluation. Provider outages, quota exhaustion across the whole pool and unsupported methods can still prevent a successful request. ## Decide whether the trial worked Compare the same request flow before and after the change: successful responses, p95/p99 latency, head freshness, upstream request volume and operational effort. Expand only when the evidence supports your workload. Add transaction broadcasting, historical queries and subscriptions as separate tests with their own [recovery behavior](/cloud/rpc-behavior). # Custom profiles and provider probes Source: https://docs.lasso.sh/cloud/custom-profiles Build account-owned provider pools and interpret capability probe evidence # Custom profiles and provider probes A custom profile is an account-owned pool of upstream providers. It uses the same routing engine as a managed profile while keeping its membership, credentials, billing lifecycle, and route identity separate. Start with the [two-provider evaluation](/cloud/bring-your-own-rpc), or use the [agent management workflow](/cloud/agent-flows) when available on your deployment. The agent API uses a whole-document `PUT /api/v1/management/profiles/`; read the [configuration schema](https://lasso.sh/api/v1/management/configuration-schema) and dry-run a change before storing it. Its document operation differs from the dashboard editing steps below. ## Operator journey 1. Create a draft and choose its display name. Review the generated slug before activation because the slug becomes part of the RPC route. 2. Add a chain and one or more provider URLs. Credentials remain server-side and must not appear in screenshots, issue comments, client errors, or logs. 3. Optionally probe each provider. A probe spends upstream quota because it makes representative JSON-RPC and WebSocket calls. 4. Review method categories, archive behavior, request limits, latency, and WebSocket results. Correct false assumptions before activation. 5. Activate the profile, then send a harmless request such as `eth_chainId` through its profile route. 6. Observe traffic and provider health. Add, remove, or update providers as the upstream stack changes; active edits propagate across the running cluster. An unprobed provider can be activated and routed with conservative defaults. That is useful when probe cost or provider quotas matter, but it provides less evidence for capability-aware routing. For HTTP, use an account key authorized for the account-owned profile: ```bash theme={null} curl -sS \ -H "x-lasso-api-key: $LASSO_KEY" \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' \ "https://lasso.sh/rpc/profile//load-balanced/" ``` The corresponding WebSocket route is `wss://lasso.sh/ws/rpc/profile///?key=$LASSO_KEY`. See the [live OpenAPI contract](https://lasso.sh/openapi.json) for profile-scoped HTTP, WebSocket, and authentication details. A key from another account is not authorized merely because it knows the profile slug. ## Supported provider connection settings The current Lasso Cloud editor accepts provider URLs. Use the endpoint URL supplied by your provider and verify the required methods with a probe and a test request. The editor does not currently expose custom authentication headers or independent HTTP and WebSocket endpoint pairing. A provider that requires header-only authentication, or different HTTP and WebSocket hosts or paths, cannot be fully configured through this editor. Do not assume that changing `https` to `wss` creates a valid WebSocket endpoint, or move a header credential into a URL unless your provider explicitly supports that authentication method. The management API advertises connection fields in its live configuration schema, including independent HTTP and WebSocket URLs when supported, and a `headers` map that sets secret request headers shared by that provider's HTTP and WebSocket endpoints. A `PUT /api/v1/management/profiles/` document can set, replace, or clear a provider's `headers`; omitting the field keeps the stored headers. Header names and values are validated with Lasso RPC Core's provider-header rules (a bounded count, no hop-by-hop or WebSocket handshake headers, no control characters). Reads and dry runs never return header values: `GET` reports sorted `header_names`, and dry runs report a `headers_change` of `unchanged`, `set`, `replace`, or `clear`. This header support is API-only; the dashboard editor still has no header input. Lasso RPC Core supports more transport configuration options when self-hosted; those options are separate from the current Cloud editor. ## Interpret probe results A category result summarizes representative method calls; it is not a verdict on the provider as a whole. * **Supported:** the representative methods tested successfully. * **Partial:** some methods succeeded and others failed or were unavailable. Inspect the failed methods and compare them with the application's workload. * **Blocked:** the endpoint or plan rejected the representative behavior. This can be a provider policy or plan boundary rather than a node failure. * **Not served:** Lasso intentionally does not forward the category, so no upstream test is meaningful. For example, failure of legacy uncle methods can make the core category partial without affecting ordinary Ethereum reads. `eth_createAccessList` can make state queries partial even when `eth_call`, balances, code, and storage work. Event filters may be blocked while direct `eth_getLogs` or WebSocket subscriptions remain the right application path. Judge the individual methods you need. ## Historical results Archive support is not one boolean in the upstream ecosystem. A provider can serve old blocks, state, and logs differently, and plan-gated access can look like pruning. Lasso records the probe's evidence but current routing uses a conservative archive capability model. Validate the exact historical queries and ranges your application depends on rather than assuming “reported archival” means unlimited history for every method. ## Safe editing model Profile configuration is database-backed. Successful edits invalidate and rebuild the relevant runtime configuration across regions; request routing does not query Postgres on every call. Operators are expected to test provider URLs and intended behavior before applying changes. The normal recovery path for a bad provider is to correct or remove it while the remaining eligible providers continue to serve traffic where possible. ## Block regression protection Turn **Block regression protection** **On** in a chain's settings so sequential latest-block requests never return a lower height on the same Lasso server. [Block regression protection](/advanced/block-continuity) describes its scope and errors, and [read at one block](/advanced/read-at-one-block) covers reading several values at the same block. ## Live block monitoring In a chain's settings, **Live block monitoring** uses the providers selected for **Monitor new heads**. Turning it off preserves those selections and uses HTTP polling; client subscriptions remain available. Shared providers may still have feeds in use by another profile. Open **Learn more** for polling controls and current status. Custom intervals are available when Lasso recognizes credentials in the provider URL. Detection uses URL patterns; a private endpoint with an unrecognized short path token still receives the managed polling rate. Endpoints classified as public retain Lasso's minimum polling interval. Monitoring tracks provider freshness, while **Block regression protection** controls the blocks returned to callers. # Features and boundaries Source: https://docs.lasso.sh/cloud/features A compact reference for Lasso Cloud capabilities and workload boundaries # Features and boundaries ## RPC integration * Standard JSON-RPC 2.0 over HTTP and raw WebSocket; no Lasso SDK is required. * A versioned Ethereum compatibility target with strict client envelopes, standards-shaped Lasso errors, transparent valid upstream results and errors, and explicit provider capability differences. * Chain names or numeric chain IDs on supported routes. * Explicit `load-balanced`, `latency-weighted`, `fastest`, and `priority` URL paths where profile policy permits them. * Provider-override routes for targeted testing and diagnosis. Read [Ethereum JSON-RPC compatibility](/cloud/json-rpc-compatibility) for the precise unification contract and claim boundaries. ## Routing and resilience * Capability-aware filtering before provider selection. * Recent routing latency evidence by provider, transport, and bounded method family. Regional metrics are available separately. * Circuit and rate-limit state influence the attempt order. * Sequential attempts on failures that are safe to retry. * Conservative single-dispatch handling for signed transactions, unknown methods, and provider-local work. * Shared upstream WebSocket connections with within-profile failover. * Ordered fallback phases for managed system profiles where configured. Read [Routing and failure semantics](/cloud/routing) before interpreting strategy names as hard distribution or availability guarantees. ## Block heights and consistent state reads * [Block regression protection](/advanced/block-continuity) prevents sequential latest-block requests from returning lower heights on the same running Lasso server. Turn it On in a custom profile; fleet sharing is automatic and recovery after a server switch or restart is best effort. * [Read at one block](/advanced/read-at-one-block) uses a standard block-hash selector when several balances or contract reads must refer to the same block. It works with protection Off. Ordinary `latest` state calls remain unpinned. ## Managed and custom profiles * Managed profiles provide a ready-to-use provider pool. * Account-owned profiles can combine commercial provider accounts and self-hosted nodes across chains. * Provider probes inspect representative methods, history, limits, latency, and WebSocket behavior; they are optional and consume upstream requests. * Database-backed edits propagate to regional runtime configuration without a database query on the request hot path. Provider URLs and credentials are sensitive. Never expose them in logs, screenshots, issues, telemetry labels, or client-visible errors. ## Keys and access * Anonymous keys can be created without signup and later claimed by an account without changing the RPC endpoint. * Account keys support dashboard management, profile authorization, and shared account usage. * Entitlements, balances, and rate limits are enforced before upstream routing. * Any key can receive prepaid premium credit with USDC over an enabled Tempo MPP or Base x402 rail through a standard 402 exchange. An account-owned key can buy time-limited Custom access. Stripe-backed account billing is separate. Legacy MPP session RPC is disabled. The live [service manifest](https://lasso.sh/agent.json) lists enabled rails; see [Operate RPC with an agent](/cloud/agent-flows). * Read `pricing` in the live [service manifest](https://lasso.sh/agent.json) for anonymous method prices and the [account plans](https://lasso.sh/pricing) for account billing. Those CU models are different; see [Usage units](/cloud/observability#usage-units). ## Evidence * `x-lasso-profile` identifies the authorized profile on authenticated routes. * Anonymous-key and paid-session responses can include method-based CU and USD charge headers. Account-key usage is recorded by the service but those responses do not currently expose per-request CU or USD headers. * Optional routing metadata is available in response headers or the JSON body. * The dashboard shows topology, provider health, regional metrics, recent activity, and account usage. Recent activity is bounded and not a durable request ledger. See [Routing and usage evidence](/cloud/observability) for exact evidence modes. ## Workload boundaries Lasso does not provide application-level signing, nonce management, transaction replacement, receipt tracking, or recreation of provider-local filter state. Archive state, old logs, transaction submission, filters, unknown methods, and WebSocket recovery each have different execution contracts. See [RPC workload behavior](/cloud/rpc-behavior). # Qualify historical RPC workloads Source: https://docs.lasso.sh/cloud/historical-workloads Measured historical-state samples, known gaps, and a practical archive-workload acceptance path. Test the exact historical methods, blocks and selectors your application needs. A successful old header or log query does not prove that an upstream can serve state at that block—or that customer routing will reach it. ## What we measured On **September 20, 2026**, we sampled the production configuration from **one SJC node**, source `89ade5b9be5745f09ee09f0c394223549e5f0085`. Direct upstream probes ran from the investigator's network, not from Lasso's deployed nodes. This is dated evidence, not a claim about a later deployment. The study made **3,404 direct requests**, including reference acquisition and retained-witness checks, without retries. Positive witnesses covered Ethereum, Optimism, Unichain, Polygon, World, Base and Arbitrum at **1/7/30/365-day** timestamp targets relative to `2026-09-20T19:21:18Z`. Tempo and Robinhood covered **1/7/30 days**; the year target predates their observed block-1 history and is inapplicable. State methods and logs used block-number and block-hash selectors; receipts used transaction hashes. Results were checked against retained positive values and block identities. Empty or null results did not establish coverage. The inventory contained 118 HTTP profile-route slots and 92 distinct endpoint authentication identities. Credentials initially matched 90 slots / 70 identities; later matching reached 103 / 78 for supplemental runs only. A credential match is not a successful probe. Unmatched or untested endpoints remain unknown. ## Direct upstream results A **cell** is one witnessed chain × depth × selector × witness. A positive cell has at least one anchored exact match, including reference-only matches. Missing witnesses are excluded. These fractions are **not uptime or overall archive coverage**. Primary profile routes are counted here; reserve results are separate. | Method | Public positive | Public with 2+ families | Premium positive | Premium with 2+ families | | - | -: | -: | -: | -: | | `eth_call` | 56/68 | 27/68 | 68/68 | 60/68 | | `eth_getCode` | 54/68 | 29/68 | 68/68 | 60/68 | | `eth_getStorageAt` | 48/56 | 26/56 | 56/56 | 48/56 | | `eth_getBalance` | 40/40 | 18/40 | 40/40 | 40/40 | | `eth_getTransactionReceipt` | 50/50 | 24/50 | 50/50 | 50/50 | | `eth_getLogs` | 54/54 | 28/54 | 54/54 | 54/54 | “Families” are observed operator groupings, generally inferred from configuration, with some endpoint-identity attribution. They do not prove independent infrastructure, accounts or quotas. Positive endpoints with unknown attribution must not be treated as proof of single-operator dependence. Receipt cells include additional positive-log transaction witnesses at the same ages. Download the [dated aggregate evidence](/files/historical-qualification-2026-09-20.json). ## Customer routing and remaining gaps A separate **119-request production Premium check matched 54/56 positive call cells** across the seven common chains. Arbitrum's day/hash call returned `-32000` and month/number returned `-32603`, although a directly tested upstream answered both witnesses. This study did **not** qualify customer routing through Public. Direct capability and successful customer routing are separate acceptance gates. Arbitrum Public direct calls matched **0/8** cells, with all three configured primary endpoints tested; Premium matched **8/8** without a second corroborating operator family. Positive Arbitrum logs did not establish historical state. Only one of three Robinhood Public endpoints was credential-matched and tested; the other two remain unknown. **BSC, testnets and early-chain history remain unqualified.** Tempo and Robinhood lack positive storage, balance, receipt and log witnesses here. World and Arbitrum lack positive balance witnesses; Base lacks a month-depth positive-log witness in the bounded search. These gaps are unknown coverage, not proof that a method is unsupported. No sustained-load or availability guarantee follows. ## Qualify your application 1. Retain known positive values, block numbers/hashes and receipt/log identities for your actual contracts, ages, storage slots and log ranges. Test both selector forms your client uses; do not replace failures with easier witnesses. 2. For providers you own or bring to Lasso, compare direct upstream results with the exact Lasso endpoint and credential. For managed profiles, qualify that Lasso endpoint and credential directly; access to the managed upstreams is not required. Inspect [routing evidence](/cloud/observability) to confirm the service and effective profile; preserve failures separately. 3. Repeat from your deployment regions under representative load and an isolated test upstream failure. Measure correctness, latency, request volume and quota use. Keep the previous endpoint available during a [staged rollout](/cloud/adoption). # RPC routing for indexers and historical backfills Source: https://docs.lasso.sh/cloud/indexer-backfills Test historical state and eth_getLogs through your actual EVM RPC route before moving an indexer or backfill behind Lasso Cloud. **Lasso Cloud · For indexer operators.** Route historical EVM reads through a profile of configured providers, then qualify the exact blocks and methods your indexer needs. A provider serving an old block header may still lack old state or logs at that block. ## Match the provider to the query Lasso considers declared and probed history evidence when admitting a request. Selected depths do not prove unlimited archive coverage. Historical state, receipts, and logs have different coverage; `eth_getLogs` also faces provider-specific block-range and result-size limits. See [RPC workload behavior](/cloud/rpc-behavior#historical-state-and-logs) for the method boundary. Build a small set of known positive queries from your own contracts and chain history. Keep the expected block hash, nonempty log or receipt identity, and expected state value. Run each query against the direct provider and the exact Lasso endpoint and credential your indexer will use. An empty response is not proof that the provider covered the requested history. | Indexer task | Trial query | Check | | - | - | - | | Rebuild state | `eth_call` or a state method at a known old block | Expected value and block selector support | | Scan events | `eth_getLogs` over a representative range | Complete known logs, range limits, and result size | | Reconcile transactions | `eth_getTransactionReceipt` for known hashes | Receipt identity and expected block | Split large log ranges to fit the upstreams you use. Keep a durable checkpoint and a reorg overlap in your indexer, then deduplicate by log identity. Test the same path from your deployment regions and under representative request volume. ## Inspect routing during a backfill The [dashboard](https://lasso.sh/dashboard) shows current provider health and recent routing activity. Opt-in [`lasso_meta`](/cloud/observability#opt-in-metadata) can identify the executing upstream when that field is available. Preserve the request ID and the query's block selector alongside your result. For HTTP batches, items route independently; a batch is neither pinned to one provider nor an atomic snapshot. Use [one block hash for related state reads](/advanced/read-at-one-block) when you need them to refer to the same block. Lasso's [dated historical qualification](/cloud/historical-workloads) shows positive samples and gaps across selected chains, depths, and methods. It is a model for testing, not a coverage promise for your contracts or a later deployment. Retain your previous endpoint until the Lasso route passes your own correctness, latency, and failure trials. # Ethereum JSON-RPC compatibility Source: https://docs.lasso.sh/cloud/json-rpc-compatibility The standards, provider differences, and claim boundaries behind Lasso's unified RPC interface # Ethereum JSON-RPC compatibility Lasso is a standards-led compatibility and routing layer for EVM RPC. Applications use ordinary JSON-RPC while Lasso handles provider differences in transport behavior, error delivery, method coverage, history, limits, and chain-specific extensions. The goal is one predictable interface. It is not a claim that every provider implements every method identically. ## Compatibility target Ethereum RPC is governed by a versioned set of specifications rather than one complete, immutable document. Lasso uses this precedence: 1. [JSON-RPC 2.0](https://www.jsonrpc.org/specification) for request, response, notification, error, and batch envelopes. 2. Applicable Final Ethereum Interface EIPs for specific wire contracts. 3. The [`ethereum/execution-apis`](https://github.com/ethereum/execution-apis) schemas pinned to commit [`739f9e00806003d2204adca7595f704849b9be30`](https://github.com/ethereum/execution-apis/commit/739f9e00806003d2204adca7595f704849b9be30). 4. The activated fork and official specification for the requested chain. 5. Observed provider capabilities for what a particular endpoint can serve. EIP-1474 remains useful for Ethereum value and error conventions, but its status is Stagnant. Lasso does not treat it as a complete Final baseline. ## What Lasso unifies ### JSON-RPC envelopes Lasso validates client envelopes before routing. `jsonrpc` must be the exact string `"2.0"`, `method` must be a string, and present `params` must be an array or object. IDs retain their JSON type and value. Valid notifications do not receive a response, and malformed requests that cannot supply a valid ID use `id: null`. HTTP batches preserve every valid item, including duplicate IDs, and omit notification responses. Use unique IDs because duplicate IDs remain ambiguous for clients even when Lasso preserves them. ### Provider results and errors Successful upstream results pass through. Lasso does not rebuild blocks, transactions, receipts, or logs because doing so could remove new fork fields or chain extensions. Some providers return a valid JSON-RPC error with an HTTP 4xx or 5xx status. Lasso preserves a request-correlated upstream `code`, `message`, and `data` object. HTML, gateway text, and other transport failures remain routing evidence and may trigger safe failover. ### Ethereum parameter conventions Lasso validates standards-defined shapes that affect routing or remove genuine ambiguity: * EIP-1898 block selectors require exactly one of `blockNumber` or `blockHash`. `requireCanonical` is valid only with `blockHash`. * EIP-234 `eth_getLogs` filters may use `blockHash`, but not together with `fromBlock` or `toBlock`. * Values authored by Lasso, including downstream subscription IDs, use canonical Ethereum quantity encoding. Provider result-shape and value-encoding checks run in tests and provider canaries rather than adding schema validation to the routing path. ## Methods, extensions, and affinity Lasso does not treat every method as interchangeable: * Reviewed replay-safe reads can use bounded failover. * `eth_sendRawTransaction` receives one upstream dispatch because a lost response may still mean the transaction was applied. * Unknown and vendor methods remain forward-compatible, but receive one dispatch unless reviewed policy marks them replay-safe. * Provider-local HTTP filter lifecycles are rejected until Lasso has an explicit affinity contract for creation, follow-up calls, owner loss, and expiry. * WebSocket subscriptions use a connection-scoped continuity path. The launch subset supports `newHeads` and `logs` with bounded recovery. Profiles can include chain extensions, bundler methods, or client-specific namespaces. Their transport, replay safety, affinity, capability, cost, and access policy must be explicit before Lasso advertises stronger behavior than single dispatch. ## Compatibility terms | Term | Meaning | | - | - | | Conformant | Lasso owns and tests the behavior against a cited requirement. | | Pass-through | Lasso preserves an upstream result or authoritative error without promising equal answers from independent upstreams. | | Capability-routed | Eligibility uses configured or observed support, which can still be incomplete or stale. | | Supported subset | Lasso enumerates the exact implemented portion of a larger interface. | | Extension | A client-, provider-, or chain-specific method is routed without being presented as standard Ethereum JSON-RPC. | | Unsupported | Lasso rejects or does not advertise the surface. Missing evidence is unknown, not proof of support or lack of support. | ## Claim boundary It is accurate to describe Lasso as a standards-led Ethereum JSON-RPC unification layer. It is not accurate to claim universal method support, identical provider semantics, conformance by every upstream, or complete implementation of every method in a rolling specification. Read [Cloud RPC workload behavior](/cloud/rpc-behavior) for the managed method, execution-safety and continuity boundaries, including rejected filter lifecycle methods. [Core supported methods](/api/supported-methods) is the separate self-hosted contract; Core forwards provider-local filters without affinity. # Route latency-sensitive EVM RPC reads Source: https://docs.lasso.sh/cloud/latency-sensitive-rpc-reads Compare Lasso Cloud routing strategies for latency-sensitive reads using your own methods, regions, provider health, and request evidence. **Lasso Cloud · For teams with latency-sensitive reads.** Keep your standard EVM JSON-RPC client and choose a routing strategy for the request path you are measuring. Provider capability and health are checked before strategy ranking. ## Choose an explicit strategy | Strategy | First-attempt behavior | Tradeoff to measure | | - | - | - | | `load-balanced` | Statistically shares first attempts inside the current eligible live tier. | Shares are not exact and health can narrow the tier. | | `latency-weighted` | Biases ordering toward providers with qualified recent successful latency. | Relative weights do not promise a fixed exploration share or exact distribution. | | `fastest` | Selects the lowest qualified recent mean successful-attempt latency for each routing decision. | Traffic can concentrate; weak evidence can fall back to availability-first ordering. | The runtime default is `load-balanced`. Name the strategy explicitly during a trial so your results describe the route you actually tested. The [routing guide](/cloud/routing) covers eligibility, health tiers, and the request deadline. ## Measure the application path Choose one read method and a representative request mix. Compare direct provider and Lasso routes from the same client regions. Record successful responses, p50/p95/p99 end-to-end latency, error categories, provider choice, head freshness, and upstream request volume. Repeat during a controlled provider failure or throttle event. A low mean upstream latency alone does not show that the app's tail latency improved. Use the [dashboard](https://lasso.sh/dashboard) for live provider state and [opt-in request metadata](/cloud/observability#opt-in-metadata) for available per-request timing and executing-channel fields. Keep missing fields unknown; candidate order alone does not prove which provider answered. The [routing-overhead benchmark](https://lasso.sh/benchmarks/routing-overhead.html) measured 0.70–1.08 ms added p95 latency in five short, isolated synthetic HTTP trials at 10,000 requests per second on a four-CPU Lasso profile. That is routing-engine overhead, not hosted capacity or Internet/provider latency. Your workload trial supplies the application result. If several reads must observe one state, use [one block hash](/advanced/read-at-one-block) across those calls. If successive `latest` results must avoid a backwards block number, evaluate [block regression protection](/advanced/block-continuity) separately. Neither strategy selection nor block policy guarantees finality. # Multi-provider RPC failover for production apps Source: https://docs.lasso.sh/cloud/multi-provider-rpc-failover Route EVM reads across configured providers, inspect the serving upstream, and plan separate recovery for signed transactions and subscriptions. **Lasso Cloud · For application and infrastructure teams.** Put configured EVM providers behind one standard JSON-RPC endpoint. Lasso selects an eligible upstream for each request and can fail over when replay is safe for that work. ## What happens to a read Lasso first filters providers by chain, method, transport, configured limits, and known health. The requested strategy orders the eligible providers. A replay-safe read can use another upstream when execution safety, dispatch certainty, failure cause, remaining deadline, and live availability permit. The entire attempt sequence stays within one request deadline and work budget. For a trial, use a method your app already calls, such as `eth_blockNumber` or an ordinary contract read. Run it through the old endpoint and Lasso under the same workload. Compare correctness, error rate, latency, head freshness, and which upstream served the response. Two configured URLs may share a vendor, quota, or underlying infrastructure, so test the failure you need to survive. ## See the provider that served the request The [dashboard](https://lasso.sh/dashboard) shows provider state and recent activity. For an individual HTTP request, opt into routing metadata with `?include_meta=body`. In the following **illustrative excerpt**, the optional `executed_channel` identifies the upstream that served a successful response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": "0x64", "lasso_meta": { "request_id": "example-request", "executed_channel": {"provider_id": "backup", "transport": "http"} } } ``` Keep the raw JSON-RPC envelope when inspecting `lasso_meta`; an SDK may return only `result`. Managed routes can withhold provider fields, and some outcomes have no executing upstream. Record absent attribution as unknown. The [routing evidence guide](/cloud/observability#opt-in-metadata) explains header mode, optional fields, and request ID correlation. ## Treat writes and streams separately Lasso sends `eth_sendRawTransaction` to one selected upstream and does not automatically retry or fan out after dispatch. If the response is lost, your application reconciles by transaction hash and owns nonce, replacement, receipt, and finality handling. Provider-local HTTP filter IDs cannot move between upstreams; use `eth_getLogs` or a supported WebSocket subscription. See the [workload behavior guide](/cloud/rpc-behavior) for the full method contract. For a surviving downstream WebSocket connection, Lasso can replace a provider and reconcile a bounded replay window within one profile. A controlled 30-minute `newHeads` test with 50 clients and 119 forced replacements recorded zero gaps or replay-created duplicates. That result covers the tested window; clients still need checkpoints and recovery after disconnection. Read the [WebSocket continuity evidence](/cloud/websocket-continuity-evidence) and [subscription recovery limits](/advanced/websocket-subscriptions#application-checkpoints-and-reorgs). ## Try one application path Start with [your existing providers](/cloud/bring-your-own-rpc) or a [managed profile](/cloud/adoption#managed-profile). Keep the previous RPC URL available while you test. The [routing guide](/cloud/routing) describes strategy and health behavior, while [versions and evidence](/releases-and-availability) separates released Lasso RPC Core from the managed deployment. # Routing and usage evidence Source: https://docs.lasso.sh/cloud/observability Correlate JSON-RPC requests with headers, metadata, dashboard state, and telemetry # Routing and usage evidence Lasso keeps the default response compatible with JSON-RPC. Evidence comes from baseline response headers, opt-in metadata, the dashboard, and server-side telemetry; those surfaces have different retention and audience boundaries. ## Baseline headers | Header | Current boundary | | - | - | | `x-lasso-profile` | Authorized profile identity on authenticated profile routes. | | `x-lasso-downgraded` | Present when account policy moved a request to public service. | | `x-lasso-effective-strategy` | Present when the requested strategy was normalized to a different allowed strategy. | | `x-lasso-cu`, `x-lasso-usd`, `x-lasso-strategy` | Per-request method-price evidence for anonymous-key responses and paid sessions. Account-key usage is recorded but these headers are not currently added to account-key responses. | | `x-lasso-balance-warning` | Present when the applicable key or downgrade policy is near/exhausting its balance. | Operational fallback between system profiles preserves the authorized service profile's `x-lasso-profile` identity. Internal routing metadata can separately describe the attempt that served the request. ## Usage units The credential and service determine what a compute unit (CU) means: | Usage path | Unit and evidence | | - | - | | Anonymous key | Method-based cost, adjusted by routing strategy. Read `pricing` in the live [service manifest](https://lasso.sh/agent.json) and applicable response headers. | | Account key | Request and response byte-based accounting with method multipliers. Account usage is recorded by the service; the anonymous method catalog and per-request USD headers do not describe this balance. | | Custom profile | Usage observations and the account's billable balance are separate. Your upstream provider can also charge for traffic. | Do not sum CU from different models or infer an account charge by multiplying anonymous-key prices by request count. Retain the credential class, profile, request count, measurement window, and available service usage evidence when comparing a client run with the dashboard. Dashboard freshness and retained request evidence do not establish immediate settlement. ## Opt-in metadata Use `?include_meta=headers` or `X-Lasso-Include-Meta: headers` to preserve the JSON-RPC body and receive: * `x-lasso-request-id`; * `x-lasso-meta`, a base64url-encoded JSON routing record when it fits within the header limit. ```bash theme={null} curl -sS -D /tmp/lasso-headers.txt \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \ "https://lasso.sh/rpc/k/$LASSO_KEY/fastest/ethereum?include_meta=headers" python - <<'PY' import base64, json, pathlib headers = pathlib.Path("/tmp/lasso-headers.txt").read_text().splitlines() encoded = next( line.split(":", 1)[1].strip() for line in headers if line.lower().startswith("x-lasso-meta:") ) encoded += "=" * (-len(encoded) % 4) print(json.dumps(json.loads(base64.urlsafe_b64decode(encoded)), indent=2)) PY ``` Use `include_meta=body` only when the client accepts a changed response shape. That mode adds a top-level `lasso_meta` object; it does not add `x-lasso-meta`. The metadata fields are defined once, in [request metadata](/observability/request-metadata#metadata-fields). On Lasso Cloud, `service_profile_id` identifies the original service profile and `profile_id` the effective route, including configured fallback. Client metadata does not include per-attempt error details. Provider fields can be withheld for managed profiles or unavailable on a request path. A selected candidate is not proof that it served the final response. Retain missing terminal attribution as unknown; do not reconstruct it from candidate order or dashboard health. WebSocket callers can add `"lasso_meta":"notify"` to a JSON-RPC request to ask for a follow-up `lasso_meta` notification. ## Block protection metadata With [block regression protection](/advanced/block-continuity) On, protected latest-block responses include a `head_policy` object. Its fields are defined once, in [block protection metadata](/observability/request-metadata#block-protection-metadata). ## Operational workflow 1. Capture the request ID at the client boundary. 2. Compare the requested route with `x-lasso-profile` and, when present, `x-lasso-effective-strategy`. 3. Decode opt-in metadata and retain the available terminal provider, transport, latency, and retry evidence. Record unavailable fields explicitly. 4. Check the dashboard's regional provider state and recent activity. 5. Correlate the request ID with application and service telemetry for a durable incident record. The dashboard's recent activity is a bounded live presentation stream, not a durable request ledger. A connected LiveView receives relevant PubSub updates; the RPC request hot path does not render dashboard state or query Postgres for each viewer. ## Common status signals | Signal | Meaning | | - | - | | `401` | API key is missing or invalid. | | `402` | Anonymous balance is exhausted or a credit or Custom-access purchase requires payment. | | `403` | The key is not authorized for the requested profile. | | `429` | A Lasso or upstream limit was reached. | | `503` | An ingress or service admission failure prevented ordinary routing. A routed JSON-RPC exhaustion error can instead arrive with HTTP 200; inspect the body. | # Lasso Cloud overview Source: https://docs.lasso.sh/cloud/overview Managed EVM RPC routing, provider pools, failover, and operational evidence # Lasso Cloud Lasso is a control layer for EVM RPC. Applications keep standard JSON-RPC clients while Lasso decides which upstream can serve each request, routes it, fails over when appropriate, and exposes evidence about the result. Use these docs to decide where Lasso belongs in an existing RPC stack and how to operate it. For exact request and response shapes, use the deployed [`/openapi.json`](https://lasso.sh/openapi.json). Fetch the live [`/agent.json`](https://lasso.sh/agent.json) for live chains, strategy names, pricing, and enabled payment rails rather than copying values into an integration. ## Choose a path * **Evaluate managed routing:** start with a Lasso-managed profile and one endpoint swap. This is the shortest way to test routing and failover without moving provider credentials. * **Bring your existing providers:** build a custom profile from Alchemy, QuickNode, Infura, self-hosted nodes, or other EVM endpoints. Lasso keeps the credentials server-side and routes across the pool. * **Operate a mixed RPC stack:** combine providers to increase quota headroom, regional reach, method or history coverage, and failure independence. Use request evidence and provider health to refine the pool over time. Read in this order: 1. [Choose an adoption path](/cloud/adoption) — choose managed pools, custom providers, or a staged combination. 2. [First endpoint quickstart](/cloud/quickstart) — make the first request and install the endpoint safely. 3. [Routing and failure semantics](/cloud/routing) — understand selection, health tiers, attempts, strategies, and fallback. 4. [Custom profiles and provider probes](/cloud/custom-profiles) — build a provider pool and interpret probe evidence. 5. [RPC workload behavior](/cloud/rpc-behavior) — understand reads, historical data, logs, writes, filters, and WebSocket boundaries. 6. [Ethereum JSON-RPC compatibility](/cloud/json-rpc-compatibility) — see the standards, unification behavior, and exact claim boundary. 7. [Routing and usage evidence](/cloud/observability) — correlate a request with routing evidence and dashboard state. 8. [Human and agent workflows](/cloud/agent-flows) — collaborate with an agent without handing it unnecessary authority. [Features and boundaries](/cloud/features) is the compact capability and boundary reference. [Multi-provider failover](/cloud/multi-provider-rpc-failover), [indexer backfills](/cloud/indexer-backfills), and [latency-sensitive reads](/cloud/latency-sensitive-rpc-reads) show how to test three common workloads against their own failure and performance requirements. [Versions, availability, and evidence](/releases-and-availability) separates released Core behavior, observed Cloud availability, and workload qualification. [Support, security, incidents, and lifecycle](/support-security-lifecycle) explains the public help path, current best-effort terms, and repository boundaries. ## Product boundary Lasso owns provider eligibility, selection, health-aware attempts, and the managed control plane around profiles, keys, and usage. It does not turn every JSON-RPC operation into an interchangeable stateless read. Transaction nonce management, transaction deduplication, receipt tracking, stateful filter affinity, and workload-specific correctness remain application concerns where documented. Lasso RPC Core is the open routing engine. Lasso Cloud adds identity, keys, billing, entitlements, account-owned profiles, the managed dashboard, and agent-facing setup surfaces. ## Documentation surfaces * **Human product docs:** this site contains concepts, tradeoffs, workflows, and operational boundaries for Lasso Cloud and RPC Core. * **[`/SKILL.md`](https://lasso.sh/SKILL.md):** concise instructions an agent can execute after it understands the user's workload and receives approval for external changes. * **[`/llms.txt`](https://lasso.sh/llms.txt):** a short map to the right product and API resource. * **[`/openapi.json`](https://lasso.sh/openapi.json):** exact machine-readable HTTP contracts. * **[`/agent.json`](https://lasso.sh/agent.json):** service discovery metadata. # First endpoint quickstart Source: https://docs.lasso.sh/cloud/quickstart Create a key, make a harmless RPC call, and stage the first endpoint change # First endpoint quickstart This flow evaluates Lasso with a standard JSON-RPC call. Agents should operate within the user’s existing resource and spending authority. For a managed endpoint with USDC top-ups and owner handoff, follow [Operate RPC with an agent](/cloud/agent-flows). ## 1. Check live support ```bash theme={null} curl -sS https://lasso.sh/agent.json curl -sS https://lasso.sh/openapi.json ``` Choose a chain from `chains` in the manifest and an explicit strategy. Read `pricing` and `payments` there before any purchase. The runtime default is `load-balanced`, but an explicit URL makes the evaluation reproducible. ## 2. Create and save a key Check `auth.keys.creation_enabled` in the manifest, then create a key: ```bash theme={null} curl -sS -X POST https://lasso.sh/api/v1/management/keys \ -H 'content-type: application/json' -d '{"name":"my-app"}' ``` The response includes `key`, `rpc_url`, and `management_token`. Put the RPC key in the project's existing secret store and the management token in the agent's private store. Do not print either into chat, logs, shell history, screenshots, or source control. Load the key from the project's secret manager. For a one-off interactive shell, read it without echoing it or placing the value in shell history: ```bash theme={null} read -rsp "Lasso key: " LASSO_KEY && export LASSO_KEY && printf '\n' ``` ## 3. Make a harmless call ```bash theme={null} curl -sS -D /tmp/lasso-headers.txt \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' \ "https://lasso.sh/rpc/k/$LASSO_KEY/load-balanced/ethereum?include_meta=headers" ``` The body remains standard JSON-RPC. `include_meta=headers` adds a request ID and encoded routing metadata without changing the body shape. Anonymous keys can also receive per-request CU and USD headers. See [Routing and usage evidence](observability) before building automation around a header. ## 4. Install the URL Lasso replaces the upstream URL; the client remains standard EVM JSON-RPC. ```bash theme={null} export RPC_URL="https://lasso.sh/rpc/k/$LASSO_KEY/load-balanced/ethereum" ``` For viem: ```javascript theme={null} import { createPublicClient, http } from "viem"; import { mainnet } from "viem/chains"; const client = createPublicClient({ chain: mainnet, transport: http( `https://lasso.sh/rpc/k/${process.env.LASSO_KEY}/load-balanced/ethereum`, ), }); ``` Begin with a non-critical stateless read path. Compare results, latency, and routing evidence before expanding traffic or adding historical, write, filter, or subscription workloads. ## 5. Link management when useful An anonymous key can be linked to a dashboard account without changing its RPC URL. Use the returned key `id` and management token: ```bash theme={null} curl -sS -X POST \ -H "Authorization: Bearer $LASSO_MANAGEMENT_TOKEN" \ "https://lasso.sh/api/v1/management/keys/$LASSO_KEY_ID/claim-link" ``` Give the short-lived `claim_url` to the user. Claiming keeps each key created by that token and its RPC URL, moves remaining purchased credit to the account’s shared balance, and ends anonymous management. Grant later agent management and funding access separately; claiming does not broaden the app key. For exact key, credit purchase, Custom access, and claim payloads, use [`/openapi.json`](https://lasso.sh/openapi.json) and [`/SKILL.md`](https://lasso.sh/SKILL.md). # Routing and failure semantics Source: https://docs.lasso.sh/cloud/routing Understand provider eligibility, strategies, health tiers, attempts, and fallback # Routing and failure semantics For each request, Lasso finds the providers in the profile that can serve it, orders them by health and by the strategy in your URL, and tries them in turn until one returns an acceptable response. A strategy chooses within the eligible, similarly healthy set; it doesn't force traffic to an unhealthy or incapable provider. The routing engine's selection, health tiers and circuit breakers are defined once, in [provider selection](/concepts/provider-selection) and [circuit breakers](/concepts/circuit-breakers). ## Strategies The default is `load-balanced`. During an evaluation, include a strategy segment explicitly so the integration does not depend on an implicit default. | Strategy | Use it when | | - | - | | `load-balanced` | Comparable providers should share first attempts and quota. | | `latency-weighted` | You want a performance bias without always selecting one provider. | | `fastest` | Latency matters enough to accept traffic concentration. | | `priority` | You want your Custom profile's provider order as the first preference. | [Routing strategies](/concepts/routing-strategies) defines each strategy precisely. `load-balanced` doesn't guarantee an even split, and `fastest` can concentrate traffic on one provider for one kind of request. ## Failover Lasso advances to the next eligible provider for failures classified as safe to retry. A routed JSON-RPC exhaustion error can be returned in an HTTP 200 response; authentication and admission failures can use distinct HTTP statuses. Inspect both the HTTP status and the JSON-RPC `error` object. The number of providers in a profile is therefore not itself a guarantee: providers must overlap on the requested capability and remain in usable health states. System profiles can have ordered fallback phases. Premium is designed to fall back to public after premium-provider exhaustion so an eligible request has the greatest chance to succeed. The request retains its authorized service-profile identity even when an internal fallback phase serves it. Custom profiles do not implicitly borrow a different account's providers. Provider-override routes are diagnostic: they select one explicit provider and do not use cross-profile fallback. ## What failover does not imply Failover does not make provider-local state portable. A filter ID created by one provider, an application-managed transaction sequence, or a provider-specific extension can require affinity or application recovery. Read [RPC workload behavior](/cloud/rpc-behavior) before treating those workloads like stateless reads. For implementation-level algorithms and tuning, see RPC Core's [provider selection](/concepts/provider-selection). # RPC workload behavior Source: https://docs.lasso.sh/cloud/rpc-behavior Plan routing for reads, historical state, writes, filters, and WebSocket subscriptions # RPC workload behavior Routing guarantees depend on the operation. Classify the workload before choosing a rollout and recovery plan. ## Stateless reads Methods such as `eth_chainId`, `eth_blockNumber`, current balances, transaction receipts, and many contract calls are the cleanest failover candidates. Lasso can try another eligible provider when a failure is safe to retry. Applications must still tolerate ordinary chain reorganization and provider freshness differences. ## Historical state and logs Historical block, state, and log coverage varies independently across provider plans. Lasso analyzes block references and provider capabilities, but a probe is evidence from selected depths and representative calls—not proof of unlimited history. `eth_getLogs` also has provider-specific block-range and result-size limits. Split large ranges, paginate with an overlap appropriate for reorg handling, and verify the exact range used by an indexer. A provider that serves old logs may still lack old state, and the reverse is also possible. When Lasso recognizes a range or result-size limit, the JSON-RPC error has `code: -32005` and `data: {"reason": "log_range_too_large", "action": "reduce_block_range"}`. Make the next query over a smaller block span. Lasso does not split it automatically or repeat the same query against another provider. A key-authenticated request that reaches Cloud metering can still consume usage when it returns this error; check the key's response charge or account usage rather than assuming failed requests are free. See the [error-code contract](/api/error-codes#large-eth_getlogs-queries). See [historical workload qualification](/cloud/historical-workloads) for dated positive-witness results, known gaps and an application acceptance path. ## Trace and debug methods Trace namespaces are often plan-gated or client-specific. Include only providers that support the exact trace method and parameters your application sends. Treat a successful representative probe as a starting point, then test real payload shapes and timeouts. ## Transaction broadcast `eth_sendRawTransaction` sends the signed bytes to one selected upstream under one deadline. Lasso does not automatically retry or fan out after dispatch because a lost response may still mean the transaction was applied. Production senders should own signing, nonce allocation, idempotency, reconciliation by transaction hash, rebroadcast policy, and receipt/finality tracking. ## Stateful HTTP filters Lasso currently rejects `eth_newFilter`, `eth_newBlockFilter`, `eth_newPendingTransactionFilter`, `eth_getFilterChanges`, `eth_getFilterLogs`, and `eth_uninstallFilter` locally with `-32601`. Their IDs are provider-local, and Lasso does not yet have an accepted affinity lifecycle for creation, follow-up calls, owner loss, and expiry. Use `eth_getLogs` for historical scans and WebSocket subscriptions for real-time delivery. ## WebSocket subscriptions Lasso accepts `newHeads` and `logs` subscriptions, not pending transactions, and can move them to another provider within the profile, backfilling the gap over HTTP. Recovery is bounded; when it can't complete, the client's connection closes explicitly. How recovery works, and what your application must still checkpoint, is defined once, in [WebSocket subscriptions](/advanced/websocket-subscriptions). Subscriptions don't fall back across profiles. Clients must reconnect after a network interruption, handle duplicate or delayed events, and keep their own durable checkpoint for indexer-grade processing. # WebSocket continuity evidence Source: https://docs.lasso.sh/cloud/websocket-continuity-evidence Scope and result of Lasso's controlled newHeads provider-replacement soak. Lasso tested `newHeads` subscription recovery during provider replacement on a surviving downstream WebSocket connection. This is a controlled continuity test, not a guarantee across client disconnections, application-node loss, or exhausted recovery bounds. Applications should keep checkpoints and reconcile after reconnecting. See [subscription recovery limits](/advanced/websocket-subscriptions#application-checkpoints-and-reorgs). ## Test and result On August 22, 2026, a pinned Lasso implementation ran a 30-minute in-process soak. Fifty downstream clients shared one upstream `newHeads` subscription. The harness forced a provider replacement every 15 seconds and skipped three live blocks per replacement so HTTP replay had to restore the gap. It recorded 119 replacements, zero downstream gaps, and zero replay-created duplicates. The coordinator count stayed at one. Subscription state drained after the last client left, within the test's 95-second teardown limit. ```text theme={null} WS_SOAK_EVIDENCE {"duration_ms":1800000,"failovers":119,"clients":50,"gaps":0,"duplicates":0,"teardown_ms":89008,"final_head":9387,"warm_processes":660,"final_processes":660,"warm_memory_bytes":94445512,"final_memory_bytes":92216898,"peak_coordinators":1,"peak_backfill_owners":0,"peak_event_buffer":0,"peak_dedupe":155} ``` This is a Lasso-run gate against implementation commit `89ad1a09ad88a1cf86a1dbc8ac5de6feb526b5f3`. Its Cloud harness is not part of the public RPC Core release, so this result is reported evidence, not a command you can reproduce from the public repository. It measures bounded replacement within one profile and one running process. It does not measure a general hosted capacity, SLA, or exactly-once delivery guarantee. # Architecture Source: https://docs.lasso.sh/concepts/architecture Request routing, shared upstream resources, and regional observability in RPC Core. Lasso RPC Core is an Elixir/Phoenix application. Clients use HTTP JSON-RPC or WebSocket endpoints; the dashboard reports provider and request observations. ## Request path A request resolves its profile and chain, applies transport and method policy, filters eligible providers, and selects an upstream using the requested strategy. Execution safety and a request deadline bound further attempts. A supported method is still subject to the selected provider's plan, parameters, history, and current capacity. Signed transaction submission receives one upstream dispatch. A lost response can be indeterminate; clients reconcile the transaction hash. See [Supported methods](/api/supported-methods). ## Runtime ownership Profiles own routing configuration and client subscription scopes. Identical upstreams can share physical HTTP/WebSocket resources, health observations, and circuit breakers. Shared runtime reduces duplicated work; it does not create quota isolation. See [Profiles and reload](/concepts/profiles). Subscriptions are multiplexed for matching streams and use bounded recovery. Ordinary RPC requests do not require an upstream subscription. See [WebSocket endpoints](/api/websocket-endpoints). ## Multiple nodes Each node routes from local upstream measurements. Clustering aggregates observability; it does not forward client RPC requests between nodes. Configure a load balancer to reach the desired Lasso region. Each node needs its own matching profile files and reload. See [Clustering](/deployment/clustering). ## Product boundary RPC Core does not provide Cloud accounts, billing, API-key authentication, or incoming customer quotas. Protect all externally exposed endpoints through your deployment boundary. [Lasso Cloud](/cloud/overview) provides the managed product. # Circuit breakers Source: https://docs.lasso.sh/concepts/circuit-breakers Understand transport health, temporary backoff, and shared provider state. Circuit breakers temporarily exclude failing upstream transports and admit recovery attempts before returning them to ordinary routing. HTTP and WebSocket health are tracked separately. | State | Meaning | | - | - | | Closed | Normal admission, subject to the other routing gates. | | Open | Ordinary traffic is excluded until recovery is permitted. | | Half-open | Limited recovery traffic determines whether service has recovered. | A closed circuit alone does not prove that a provider is currently eligible. Connection state, rate-limit cooldowns, lag, method restrictions, and request parameters can still exclude it. ## Rate limits and shared providers Rate-limit backoff is a separate signal from circuit failure. A provider can be temporarily rate limited while its circuit remains closed. Identical upstreams shared across profiles share runtime signals; creating another profile does not bypass the provider's real quota. Inspect the affected transport and region. An aggregate warning may represent only part of the deployment. Do not interpret a regional problem as proof that every region is unavailable. ## Configuration ### Circuit Breaker Defaults ```elixir theme={null} config :lasso, :circuit_breaker, failure_threshold: 5, # Aggregate failure threshold; category thresholds also apply success_threshold: 2, # Consecutive successes to close recovery_timeout: 60_000 # Base ms before half-open; category/backoff rules also apply ``` Circuit settings take effect when provider instances start; restart Lasso after changing application configuration. YAML reload does not restart existing circuit breakers. See [Profiles](/concepts/profiles) for sharing behavior and [Provider capabilities](/configuration/capabilities) for error rules. # Profiles and reload Source: https://docs.lasso.sh/concepts/profiles Separate routing configurations with explicit shared-upstream behavior. Profiles define chains, providers, and routing policy. They are configuration scopes, not client authentication boundaries. RPC Core has no built-in incoming client quotas; `rps_limit` controls the dashboard tester maximum and `burst_limit` is metadata. ## Shared upstreams With `sharing_mode: auto`, identical upstreams can share physical connections, health observations, and transport circuit breakers across profiles. Separate profiles do not create separate upstream quota. A problem with the shared upstream may affect every referencing profile. `sharing_mode: isolated` separates runtime state by profile. It does not increase the upstream account's quota or remove the provider's external limits. Each profile retains its own provider membership and routing policy. Dashboard aggregate and region views describe different scopes; use a regional view when diagnosing a local failure. ## Multiple Profiles Create separate profiles for different environments or use cases: ```text theme={null} config/profiles/ ├── public.yml # Included free public providers ├── production.yml # Credentialed providers + own nodes └── staging.yml # Subset for testing ``` The dashboard lists configured profiles and links to this guide. To add a profile, create its YAML file in the profiles directory on each node and reload Lasso. For containers, follow the [deployment instructions](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/DEPLOYMENT.md#custom-profiles-and-credentials). v0.5.0 supports new profiles reusing connected WebSocket upstreams after reload. On v0.3.4, restart after adding a profile to avoid unavailable subscriptions. For new profiles and YAML edits, reload the running release: ```bash theme={null} _build/prod/rel/lasso/bin/lasso rpc 'Lasso.Config.ConfigStore.reload()' ``` Confirm the reload returns `:ok`, then refresh the dashboard. Each node reads its own files; distribute the same configuration to every node. Access via URL: `/rpc/profile/:slug/:chain` ```bash theme={null} # Included public profile curl -X POST http://localhost:4000/rpc/ethereum ... # Production profile curl -X POST http://localhost:4000/rpc/profile/production/ethereum ... ``` See [Configuration](/configuration/overview) for validated fields and [Docker deployment](/deployment/docker) for host-mounted profiles. # Provider selection and failover Source: https://docs.lasso.sh/concepts/provider-selection Eligibility, ranking and live admission in Core v0.5.0 **Lasso RPC Core v0.5.0** selects upstream channels from the requested file profile and chain. A configured provider and an executing channel are different: one provider can offer HTTP and WebSocket transports, and multiple configuration entries can refer to the same physical upstream. ```text theme={null} Configured routes → Eligible channels → Strategy rank → Health tiers → Live admission ``` ## Eligibility Selection considers the requested transport, channel availability, circuit and rate-limit state, head observations, declared archival support, method capabilities and explicit exclusions. A configured WebSocket URL alone does not establish a connected transport. Method support and parameter limits can differ between providers and transports. Historical age is inferred where the request and available head evidence allow it. A numeric selector older than `archival_threshold` requires a declared archival provider; one whose age cannot be established does not. A state read selected by block hash (EIP-1898) always requires a declared archival provider, because its age is unknown before dispatch. Without one, Core returns a non-retriable [`archive_required`](/api/error-codes#core-routing-exhaustion) error without contacting an upstream. An `archival: true` flag is configuration, not measured proof of state, receipts, logs or every selector shape. Lag selection uses Core's head observations and configured lag allowance. Unknown or stale evidence must not be interpreted as verified head agreement. See [block regression protection](/advanced/block-continuity) for the separate request-level policy and its evidence boundaries. ## Configuration This complete profile body demonstrates supported selection and provider capability fields. Add [profile frontmatter](/concepts/profiles) when saving it. The local upstream must serve Ethereum; its declared capabilities must reflect what you have qualified. ```yaml theme={null} chains: ethereum: chain_id: 1 selection: max_lag_blocks: 2 archival_threshold: 128 providers: - id: local_node url: http://127.0.0.1:8545 archival: true capabilities: unsupported_methods: - debug_traceTransaction limits: max_block_range: 1000 ``` `selection` belongs under the chain. The profile body accepts only the `chains` mapping. A `routing.method_overrides` block is not supported. See the released [file schema](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/config/file_schema.ex) and [configuration reference](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/CONFIGURATION.md#provider-capabilities). ## Strategy ranking [Routing strategies](/concepts/routing-strategies) define channel order: randomized load balancing with a bounded distinct-instance first pass for replay-safe calls, recent reliability-qualified latency, relative latency weights, or configured priority. Fastest and latency-weighted use recent local routing evidence, not lifetime dashboard averages or an implicit exploration floor. ## Health tiering The released order is: | Tier | Circuit | Rate-limited | | - | - | - | | 1 | Closed | No | | 2 | Half-open | No | | 3 | Closed | Yes | | 4 | Half-open | Yes | Open circuits are excluded. Each strategy defines its within-tier order, including [load-balanced instance diversity](/concepts/routing-strategies#load-balanced). A channel in tier 2 precedes one in tier 3 even if the latter has a better latency or configured priority. Half-open admission is controlled; appearing in the candidate order does not reserve permission to send. ## Execution and failover Core checks live admission before dispatch, so a selected channel may be rejected if its state has changed. Replay-safe requests can continue through eligible channels within the three-dispatch limit and absolute deadline. Unsafe and unknown methods do not inherit that replay behavior. See the [method contract](/api/supported-methods). A successful candidate list does not guarantee a successful request. More eligible channels may exist than the dispatch budget can reach. Routing exhaustion is a JSON-RPC error over HTTP 200; candidate or internal provider errors are not a promised public error-detail schema. ## Inspect an actual request Request [metadata](/observability/request-metadata) with `include_meta=body`. `candidate_providers` describes selection candidates, `attempted_channels` records completed attempts, and `executed_channel` identifies successful upstream execution when present. Do not count candidates or the `retries` counter as a precise list of upstream dispatches. For managed service profiles, use [Cloud RPC behavior](/cloud/rpc-behavior). This Core reference does not define Cloud entitlements, account quotas or profile fallback behavior. # Routing strategies Source: https://docs.lasso.sh/concepts/routing-strategies How released Core v0.5.0 ranks eligible upstream channels This page describes **Lasso RPC Core v0.5.0**. See [versions and evidence](/releases-and-availability) for release identity and [Cloud routing](/cloud/routing) for the managed service. ## Strategy selection Select a strategy in the request URL: | Route | Selection | | - | - | | `/rpc/:chain` | Application default; shipped as `load_balanced` | | `/rpc/load-balanced/:chain` | Randomized order with distinct-instance first pass for replay-safe calls | | `/rpc/fastest/:chain` | Recent reliability-qualified latency | | `/rpc/latency-weighted/:chain` | Weighted random order | | `/rpc/round-robin/:chain` | Alias for `load-balanced`; not deterministic round-robin rotation | | `/rpc/profile/:profile/fastest/:chain` | Explicit file profile and strategy | | `/rpc/provider/:provider_id/:chain` | One configured provider; see [provider override](#provider-override) | The chain must be configured in the profile. WebSocket routes take the same segments under `/ws/rpc`. The application default can be set to `:priority` in an Elixir configuration: ```elixir theme={null} config :lasso, :provider_selection_strategy, :priority ``` Use the base route for that default. Core does not expose a `/rpc/priority/:chain` strategy route. Strategy names in metadata use underscores, such as `load_balanced`. ## Load balanced Core starts from a randomized channel order. For replay-safe unary calls, it tries distinct **physical provider instances** before alternate transports of an already considered instance within each health tier. HTTP and WebSocket routes to the same instance do not count as two physical providers. Distinct instances do not establish independent operators or account quotas. Health tiers and explicit recovered-head preference take precedence. A healthy alternate transport still precedes a different provider in a lower tier. The lazy cursor bounds healthy-sibling deferral by its remaining candidate limit; live admission still applies when a deferred channel is selected. Alternate transports remain available, but trying another instance first can delay a working alternate on the previous instance. Unsafe and unknown methods retain their shuffled order and existing dispatch limits. Other strategies are unchanged. This ordering does not increase the three-dispatch limit or original deadline, infer archive capability, or promise equal provider shares or capacity-aware balancing. A hash selector can leave more distinct providers eligible than the dispatch budget can reach. Qualify historical reads against your actual upstreams. ## Fastest Fastest places reliability-qualified channels first, ordered by recent mean successful-attempt latency. Successful p95 and provider/transport identity break ties. Evidence is local to the routing node and partitioned by a bounded workload key, rather than a separate unbounded history for every method string. Unqualified channels remain available after qualified channels. When no channel qualifies, Core reports availability degradation and uses its available latency priors and deterministic identity order. Missing or stale evidence does not become proof that a provider is fast or reliable. Lifetime dashboard averages are not the routing score. ## Latency weighted Latency weighted also places reliability-qualified channels first. Within a measured group, it calculates relative weights: ```text theme={null} weight = (best_mean / candidate_mean)^beta ordering key = -log(U) / weight ``` Lower ordering keys come first. Reliability determines qualification; it is not a multiplier in this formula. There is no latency floor, weight floor or implicit exploration share. Unmeasured channels are shuffled after measured channels. If nothing qualifies, available recent latency priors still order the fallback group; a wholly unmeasured group is shuffled uniformly. The released latency control is `LW_BETA`, a positive number with default `3.0`: ```bash theme={null} export LW_BETA=3.0 ``` ## Priority Lower provider `priority` values come first within the same health tier. Priority sets configured precedence; it does not override health or execution-safety checks. This is a complete profile **body** for two local HTTP upstreams. Add the profile frontmatter described in [profiles](/concepts/profiles) when saving a profile file. The upstreams must serve the configured chain. ```yaml theme={null} chains: ethereum: chain_id: 1 providers: - id: primary url: http://127.0.0.1:8545 priority: 1 - id: backup url: http://127.0.0.1:8546 priority: 2 ``` ## Health comes first Health tiers take precedence over strategy rank, and open circuits are excluded. Each strategy orders channels within a tier. The tiers and live admission are defined in [provider selection](/concepts/provider-selection#health-tiering). ## Per-method routing Choose different URL strategies for different requests. Profile YAML does **not** accept `routing`, `default_strategy` or `method_overrides` blocks. Provider `capabilities` can exclude methods and declare range/history limits; see [provider selection](/concepts/provider-selection#configuration). These restrictions do not select a per-method strategy or prove provider capability. WebSocket URL strategies apply to forwarded JSON-RPC calls. `eth_subscribe` uses the shared subscription pool's priority-based selection independently of the URL strategy. An explicit provider URL constrains a subscription to that provider. ## Provider override Use `/rpc/provider/:provider_id/:chain` to target a configured provider. The profile-scoped form is `/rpc/profile/:profile/provider/:provider_id/:chain`. Identity and live admission checks still apply. A provider override is useful for diagnosis; it is not proof that normal routing would choose that provider. ## Execution and failover A strategy only orders candidates. Dispatch limits, deadlines and which methods may fail over are defined in [provider selection](/concepts/provider-selection#execution-and-failover) and the [method contract](/api/supported-methods). This contract is grounded in the released [routing guide](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/ROUTING.md), [Fastest](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/selection/strategies/fastest.ex), [LatencyWeighted](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/selection/strategies/latency_weighted.ex) and [execution envelope](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/request/execution_envelope.ex). # Provider capabilities Source: https://docs.lasso.sh/configuration/capabilities Declare method restrictions, historical limits, and provider error rules. ## Provider Capabilities Capabilities declare what a provider supports and its limits. Validated at boot time. ```yaml theme={null} capabilities: unsupported_categories: [debug, trace] unsupported_methods: [eth_getLogs] limits: max_block_range: 10000 max_block_age: 1000 block_age_methods: [eth_call, eth_getBalance] error_rules: - code: 35 category: capability_violation - message_contains: "timeout on the free tier" category: rate_limit ``` | Field | Type | Description | | - | - | - | | `unsupported_categories` | list | Method categories this provider doesn't support (e.g., `debug`, `trace`) | | `unsupported_methods` | list | Specific methods this provider doesn't support | | `limits.max_block_range` | integer | Max block range for `eth_getLogs` | | `limits.max_block_age` | integer | Max block age for state methods (pruned provider) | | `limits.block_age_methods` | list | Methods subject to `max_block_age` limit | | `error_rules` | list | Provider-specific error classification overrides (first match wins) | When `capabilities` is omitted, defaults to permissive (only `local_only` methods blocked, no limits). These declarations constrain routing; they are not a proof of universal upstream compatibility. Test representative successful requests and known failure cases before relying on a provider-specific rule. Use the narrowest reliable match. Provider APIs, plans, and error messages can change. Discovery produces configuration for review. RPC Core does not promise unsupervised adaptation of every method, parameter, or historical limit. Changes to YAML require a successful reload on each running node. See [Profiles](/concepts/profiles). # Environment variables Source: https://docs.lasso.sh/configuration/environment-variables Runtime variables for RPC Core containers and source deployments. ## Environment Variables Reference ### Core | Variable | Required | Description | Default | | - | - | - | - | | `SECRET_KEY_BASE` | Prod | Phoenix signing secret (64+ bytes) | - | | `PHX_HOST` | Recommended in prod | Public hostname for generated URLs | `localhost` | | `PHX_SCHEME` | No | External URL scheme | `https` in production | | `PHX_SERVER` | No | Set to `true` to enable the HTTP server when the endpoint configuration does not already enable it; production enables it by default | - | | `PORT` | No | HTTP listener port | `4000` | | `LASSO_NODE_ID` | Prod | Unique node identifier | `"local"` in dev | | `LASSO_DATA_DIR` | No | Base directory for profiles and snapshots | `/data` in Docker; unset outside Docker | | `LASSO_PROFILES_DIR` | No | Profile YAML directory; overrides the data directory | `config/profiles` outside Docker | | `LASSO_SNAPSHOTS_DIR` | No | Snapshot directory, selected at runtime | `priv/benchmark_snapshots` outside Docker | | `LASSO_VM_METRICS_ENABLED` | No | Set to `true` to enable VM metrics | `false` | | `LASSO_COWBOY_TELEMETRY_ENABLED` | No | Set to `false` to disable Cowboy per-request telemetry; Lasso application and dashboard events remain enabled | `true` | | `LASSO_HTTP_RESPONSE_HEAP_TUNING_ENABLED` | No | Use a larger short-lived heap while validating completed HTTP upstream responses; benchmark before enabling for latency-bound traffic | `false` | | `LASSO_HTTP_POOL_SIZE` | No | Maximum HTTP/1 connections per upstream host and pool | `256` | | `LASSO_HTTP_POOL_COUNT` | No | Independent Finch pools per upstream host | `1` | ### Clustering | Variable | Required | Description | | - | - | - | | `CLUSTER_DNS_QUERY` | For clustering | DNS name for node discovery | | `CLUSTER_NODE_BASENAME` | For clustering | Erlang distribution node basename | | `RELEASE_DISTRIBUTION` | For clustering | Set to `name` for long node names | | `RELEASE_NODE` | For clustering | `@` | | `RELEASE_COOKIE` | Release/container | Private distribution secret; identical across cluster members | ### Provider Keys Any `${VAR_NAME}` in profile YAML is resolved from environment variables at startup. *** Environment changes require a process restart or container recreation. YAML reload reads the environment of the running process. See [Docker deployment](/deployment/docker). ### Block continuity Global mode requires `LASSO_BLOCK_PUBLICATION_DATABASE_URL`, `LASSO_BLOCK_PUBLICATION_MEMBERS`, and a unique stable `LASSO_NODE_ID` on every serving instance. Ordinary routing needs no database. Follow the complete [journal and fleet setup](/deployment/block-continuity) before enabling it. # Configuration overview Source: https://docs.lasso.sh/configuration/overview Configure routing profiles without confusing them with access or quota boundaries. Lasso is configured via YAML profile files in `config/profiles/`. Each profile defines chains, providers, routing policy, and dashboard tester settings. Multiple profiles provide separate routing configurations for different environments. Identical upstreams can share connections, health, and observations; profiles are not authentication boundaries. ## Profile File Structure ```yaml theme={null} # config/profiles/.yml # --- Frontmatter (YAML document separator) --- --- name: "My Profile" # Display name slug: "my-profile" # URL identifier (used in /rpc/profile/:slug/...) rps_limit: 100 # Dashboard tester maximum RPS burst_limit: 500 # Metadata; not enforced by OSS --- # --- Body: Chain configurations --- chains: ethereum: chain_id: 1 providers: - id: "my_provider" url: "https://..." ``` **Frontmatter fields:** | Field | Type | Required | Description | | - | - | - | - | | `name` | string | Yes | Human-readable profile name | | `slug` | string | Yes | URL-safe identifier. Must be unique across profiles | | `rps_limit` | integer | No | Maximum RPS offered by dashboard tester controls (default: 100) | | `burst_limit` | integer | No | Profile metadata (default: 500); no OSS ingress enforcement | OSS does not authenticate clients or enforce per-client request quotas. Configure authentication and inbound rate limiting at your reverse proxy. Provider quota and circuit-breaker backoff are separate routing controls. Unknown YAML fields and invalid types are rejected. Use booleans (`false`), not quoted strings (`"false"`). A failed reload keeps the previous active configuration. Errors identify the field without printing credential values. Profile slugs must match their `.yml` filenames. Files beginning with `_` or `.` are skipped. Keep a `public.yml` profile: routes without a profile use `public`, and it is required at startup. `unlisted: true` hides a profile from the selector; its endpoints remain accessible. ## Next steps * [Profile and chain settings](/configuration/profiles) * [Provider settings and credentials](/configuration/providers) * [Capability declarations](/configuration/capabilities) * [Reload and profile sharing](/concepts/profiles) # Profile configuration Source: https://docs.lasso.sh/configuration/profiles Validated profile metadata, chain monitoring, selection, and WebSocket settings. ## Profile File Structure ```yaml theme={null} # config/profiles/.yml # --- Frontmatter (YAML document separator) --- --- name: "My Profile" # Display name slug: "my-profile" # URL identifier (used in /rpc/profile/:slug/...) rps_limit: 100 # Dashboard tester maximum RPS burst_limit: 500 # Metadata; not enforced by OSS --- # --- Body: Chain configurations --- chains: ethereum: chain_id: 1 providers: - id: "my_provider" url: "https://..." ``` **Frontmatter fields:** | Field | Type | Required | Description | | - | - | - | - | | `name` | string | Yes | Human-readable profile name | | `slug` | string | Yes | URL-safe identifier. Must be unique across profiles | | `rps_limit` | integer | No | Maximum RPS offered by dashboard tester controls (default: 100) | | `burst_limit` | integer | No | Profile metadata (default: 500); no OSS ingress enforcement | OSS does not authenticate clients or enforce per-client request quotas. Configure authentication and inbound rate limiting at your reverse proxy. Provider quota and circuit-breaker backoff are separate routing controls. Unknown YAML fields and invalid types are rejected. Use booleans (`false`), not quoted strings (`"false"`). A failed reload keeps the previous active configuration. Errors identify the field without printing credential values. Profile slugs must match their `.yml` filenames. Files beginning with `_` or `.` are skipped. Keep a `public.yml` profile: routes without a profile use `public`, and it is required at startup. `unlisted: true` hides a profile from the selector; its endpoints remain accessible. ## Chain Configuration Each chain is a key under `chains:` with the following structure: ```yaml theme={null} chains: ethereum: chain_id: 1 name: "Ethereum Mainnet" block_time_ms: 12000 monitoring: probe_interval_ms: 12000 lag_alert_threshold_blocks: 5 selection: max_lag_blocks: 1 archival_threshold: 128 websocket: subscribe_new_heads: true new_heads_timeout_ms: 35000 failover: max_backfill_blocks: 100 backfill_timeout_ms: 30000 ui-topology: color: "#627EEA" size: xl providers: - id: "ethereum_llamarpc" # ... ``` ### Chain Fields | Field | Type | Required | Description | | - | - | - | - | | `chain_id` | integer | Yes | EIP-155 chain ID | | `name` | string | No | Display name (defaults to chain key) | | `block_time_ms` | integer | No | Average block time in milliseconds. Used for optimistic lag calculation and the continuity freshness bound | | `head_policy` | string | No | `off` (default), `local` (one process), or `global` (enrolled fleet). Global mode requires the [publication journal and fleet setup](/deployment/block-continuity) | ### Monitoring Controls probe frequency and the dashboard lag status threshold. Shared upstreams use the shortest configured probe interval across profiles; HTTP polling slows while WebSocket block updates are active. | Field | Type | Default | Description | | - | - | - | - | | `probe_interval_ms` | integer | 12000 | Health check polling interval. Set to \~1x block time for L1, \~2.5x for L2 | | `lag_alert_threshold_blocks` | integer | 3 | Dashboard lag status threshold; this setting does not emit lag warning logs | ### Selection Controls provider eligibility filtering during request routing. | Field | Type | Default | Description | | - | - | - | - | | `max_lag_blocks` | integer | unset | Exclude providers lagging more than N blocks. L1: 1-2, L2: 3-10 | | `archival_threshold` | integer | 128 | Blocks before data is considered "archival". Requests for blocks older than `head - threshold` are only routed to archival providers | ### WebSocket Controls upstream WebSocket subscription behavior. | Field | Type | Default | Description | | - | - | - | - | | `subscribe_new_heads` | boolean | true | Enable `newHeads` block tracking and eligibility for client `newHeads` subscriptions | | `new_heads_timeout_ms` | integer | 42000 | Timeout before marking subscription stale (\~3x block time) | | `failover.max_backfill_blocks` | integer | 100 | Max blocks to fetch via HTTP during subscription failover | | `failover.backfill_timeout_ms` | integer | 30000 | Timeout for backfill HTTP requests | Failover limits are captured from the active profile at the beginning of each recovery, including after YAML reload. An in-progress recovery keeps its captured limits. The backfill timeout is also bounded by the overall 30-second recovery deadline. Gaps beyond `max_backfill_blocks` terminate continuity rather than silently skipping missing blocks. ### UI Topology Dashboard visualization settings. | Field | Type | Default | Description | | - | - | - | - | | `color` | string | - | Hex color for the chain node in the dashboard topology | | `size` | string | `md` | Node size: `sm`, `md`, `lg`, `xl` | For live updates, see [Profiles and reload](/concepts/profiles). # Provider configuration Source: https://docs.lasso.sh/configuration/providers Configure upstream endpoints, credentials, runtime sharing, and monitoring. ## Provider Configuration Each provider is an entry in a chain's `providers` list. ```yaml theme={null} providers: - id: "ethereum_llamarpc" name: "LlamaRPC Ethereum" priority: 2 url: "https://eth.llamarpc.com" ws_url: "wss://eth.llamarpc.com" archival: true subscribe_new_heads: true capabilities: limits: max_block_range: 1000 ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | string | Yes | Unique provider identifier within the chain | | `name` | string | No | Display name (defaults to `id`) | | `priority` | integer | No | Lower = higher priority. Used by `:priority` strategy and as tiebreaker | | `url` | string | Yes\* | HTTP RPC endpoint URL | | `ws_url` | string | No | WebSocket RPC endpoint URL. Required for subscriptions | | `archival` | boolean | No | Whether this provider serves historical data (default: true) | | `subscribe_new_heads` | boolean | No | Override chain-level `subscribe_new_heads` for this provider | | `capabilities` | map | No | Provider capabilities (see Capabilities below) | | `sharing_mode` | string | No | `auto` shares identical upstream runtime; `isolated` separates it by profile | | `api_key` | string | No | Sends `Authorization: Bearer ` | | `headers` | map | No | HTTP request and WebSocket handshake headers, overriding defaults | | `auth_headers` | map | No | Headers with precedence over `headers` and `api_key` | \*At least one of `url` or `ws_url` is required. ## Environment Variable Substitution Provider URLs support `${ENV_VAR}` substitution. Unresolved variables reject a profile at startup or reload. Substitution also applies to `api_key`, `headers`, and `auth_headers`. ```yaml theme={null} providers: - id: "alchemy_ethereum" url: "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}" ws_url: "wss://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}" ``` ## Provider Credentials Use your own provider API keys alongside public providers: ```yaml theme={null} chains: ethereum: chain_id: 1 providers: # Your own node (highest priority) - id: "my_erigon" url: "http://my-erigon:8545" priority: 1 # Paid provider with your API key - id: "alchemy" url: "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}" priority: 2 # Free public fallback - id: "publicnode" url: "https://ethereum-rpc.publicnode.com" priority: 10 ``` In RPC Core v0.5.0, `subscribe_new_heads` controls automatic block tracking and client `newHeads` eligibility. Providers inherit the chain setting, which defaults to `true`; set a provider override to `false` when that upstream must not serve `newHeads`. A WebSocket URL is also required for subscriptions. See [Profile configuration](/configuration/profiles). Provider declarations describe intended support; validate the exact method, parameter shape, history, and plan you need against the upstream. # Operating block regression protection Source: https://docs.lasso.sh/deployment/block-continuity Configure peer sharing or optional strict fleet coordination for protected latest-block requests. Use `head_policy: local` for protection with automatic peer sharing. It requires no publication database. Use `head_policy: global` only when you need a durable floor across every admitted serving instance and accept its coordination costs. See [Block regression protection](/advanced/block-continuity) for the affected JSON-RPC methods and guarantees. ## Protection with automatic peer recovery Set `head_policy: local` in each chain's file profile to enable protection with automatic progress sharing across connected peers. Local routing needs no PostgreSQL publication database. Each application retains its accepted floor in memory; connected BEAM peers exchange height hints in the background. Those hints prefer suitable providers without blocking requests or removing valid fallback providers. A worker restart retains application-owned state. A complete application restart, node switch, partition or loss of all copies can lose continuity. Recovery has no promised time or block-gap bound, and the total profile/chain inventory affects propagation. Qualify actual provider capacity: a protected latest-block request still fails when every eligible provider is unavailable. If your application also reads state at a chosen hash, qualify selector support and retained state for those methods separately. If this installation previously enrolled Global scopes, complete the coordinated disable procedure below before changing their contract. Retain its journal and member configuration so old durable history continues to be reconciled. ## Strict fleet coordination `global` mode stores the monotonic floor, selected block and admitted instance boots in PostgreSQL. Serving requests read local ETS grants. PubSub accelerates updates; background reconciliation recovers missed messages. PostgreSQL is an optional dependency for this policy, not for ordinary Core routing. Read the [request behavior](/advanced/block-continuity) before enabling Global mode. Global mode can return an availability error to preserve continuity. It does not choose one block automatically for unrelated state calls. The remaining procedures apply to Global mode. ## Install the journal Use one durable PostgreSQL database shared by every serving instance. Use the same journal throughout the lifetime of each protected file-profile identity. Start with `head_policy: off` while installing and validating infrastructure. Provide these environment variables through your deployment's secret store: ```text theme={null} LASSO_BLOCK_PUBLICATION_DATABASE_URL= LASSO_BLOCK_PUBLICATION_MEMBERS=iad-1,sjc-1 LASSO_NODE_ID=iad-1 ``` Each process needs a stable, unique `LASSO_NODE_ID`; the member list names **every instance accepting protected traffic**, including multiple instances in one region. All instances use the same initial member list. On the SJC instance, set `LASSO_NODE_ID=sjc-1`. A replacement process receives a new boot identity. Unknown or unfenced replacement boots cannot serve protected block choices. Run migrations once using the new artifact and the database environment: ```sh theme={null} # Source checkout mix lasso.block_publication.migrate # Built release (also usable with a container exec/one-off command) bin/lasso eval 'Lasso.BlockPublication.Storage.migrate()' ``` This command opens no RPC ingress. Repeating it applies only pending migrations. It creates the journal and capacity ledger; ordinary installations need neither the command nor a PostgreSQL server. The first migration does not import a local instance's volatile floor. During enrollment, each live instance contributes its accepted local floor; a previously stopped local-only service has no durable cross-restart guarantee to recover. The journal requires acknowledged transactions to survive database failover. Retain the database, backups and schema along with the service's identity. A point-in-time restore before an acknowledged floor cannot safely resume that same contract. Stop protected ingress and reconcile the highest retained floor before recovery; do not reset rows to fix an availability incident. ## Enable and verify In the desired profile's YAML chain, configure: ```yaml theme={null} chains: ethereum: chain_id: 1 block_time_ms: 12000 head_policy: global # Keep this profile's configured providers and other chain settings. ``` Distribute/reload the file on every enrolled instance. First enrollment happens in the background; a successful config load is not proof that a grant is ready. Verify that your providers can serve protected latest-block requests within the freshness limit. If your application also reads state at a selected block hash, separately verify selector support and availability of that state. Request `eth_getBlockByNumber` with `["latest", false]` and `?include_meta=headers` through each instance's real ingress. Check the same profile/chain, `head_policy.policy=global`, `scope=profile_chain_fleet`, number, hash and serving instance. Decode the base64url `x-lasso-meta` header. If your application also reads several values at one block, test [Read at one block](/advanced/read-at-one-block) through multiple instances. Missing metadata means evidence is incomplete, not that a different block was selected. HTTP batch headers contain only one context. In a release console: ```elixir theme={null} Lasso.BlockPublication.Operator.capacity() Lasso.BlockPublication.Operator.status() Lasso.BlockPublication.Gate.read({"public", 1}) ``` A fresh grant permits local number/header responses even if a provider later lags. Advancement waits until every admitted serving boot prepares the same block and closes its prior grant. A disconnected instance can therefore stall advancement; the retained block expires after `max(60_000, 4 * block_time_ms)`. Publication changes can wait up to one second inside the RPC's existing deadline. Already hash-pinned state reads do not wait for publication. The initial operating limit is four active profile/chain scopes per journal, 1,024 retained scopes and 32 MiB of reserved state. A rejected enrollment does not change an existing floor or reservation. These conservative limits bound background work; they are not a high-throughput or broad-fleet certification. Each runtime permits at most four provider-probe tasks and four independent closure-acknowledgment tasks, with at most one closure task per scope. Gates close locally before those acknowledgment writes start. The coordinator and its task supervisor restart together, stopping old tasks before replacement work. Measure your provider costs, publication age and request error rate before expanding rollout. Do not remove the capacity constraints as a rollout shortcut. ## Changing or disabling the policy A YAML value cannot override an existing durable grant or reset its floor. For an enrolled scope, disabling requires both the desired file configuration and a durable operation: 1. Set `head_policy: off` on every instance and reload. Existing grants still govern while the durable policy is active. 2. Run `Lasso.BlockPublication.Operator.disable({"public", 1})` in a release console. Wait for the journal's `phase` to become `disabled` and confirm each serving gate is `:unmanaged`. Until every admitted boot closes or is fenced, protected block choices may return an error. 3. Keep the journal and member configuration. An instance retaining YAML `global` fails closed after durable disable until its file is updated. To reenable, restore the file's `global` value on every instance, then run `Lasso.BlockPublication.Postgres.configure({"public", 1}, "global", 12_000)`. This uses the configured roster, updates the age bound, reenables the scope and retains its floor. `Operator.enable/1` reenables without changing that bound. Verify each ingress again. Calls made while off were outside the guarantee. Do not delete or rename a protected file profile before coordinated disable. A new slug is a new service identity and does not inherit its former floor. Removing the journal environment after enrollment bypasses its recovery; keep that configuration even when every current YAML setting is off. ## Replace or remove an instance A normal application shutdown permanently closes local gates and writes boot fences before replacement. Check that it completed. If replacing an older binary without this hook, first call `Lasso.BlockPublication.Operator.quiesce_local()` on that running instance. This operation cannot be undone within the same boot. A crash or unavailable journal may prevent the shutdown fence. Stop the old process or exclude it from **all** protected ingress before recording: ```elixir theme={null} Lasso.BlockPublication.Operator.record_external_fence( {"public", 1}, "iad-1", "EXACT_OLD_BOOT", "Evidence that this boot cannot serve" ) ``` Apply it to every retained scope bound to that boot, including disabled history. A timeout or missed heartbeat is not proof that the old boot stopped serving. Never automatically fence based only on reachability. To add an instance, use `Operator.add_member(key, member)` for each relevant scope before sending it protected traffic. To remove one, quiesce/fence its boot, then use `Operator.remove_fenced_member(key, member)`. Keep environment rosters aligned with the durable roster before reenabling or creating scopes. Never route around an admission error through an unenrolled binary. ## Recovery and rollback checks Connection loss leaves existing fresh grants usable and prevents unsafe new publication. Recovery reconciles the retained floor and boots before admitting replacement processes. Once a grant expires, new block choices fail until fresh publication resumes. Normal state requests retain their selectors and ordinary provider retry budgets. Before release, exercise journal interruption, worker restart, graceful and unfenced process replacement, and disable/re-enable. Include hash-pinned state reads if your application uses them. The repository CI runs PostgreSQL-backed three-BEAM tests and an Anvil/viem/SEL workflow with an actual branch replacement. It preserves correlated HTTP evidence. These tests do not certify your database provider's infrastructure failover. For rollback, coordinate disable and verify it before deploying a binary that cannot read this journal or honor its gates. Preserve journal tables and rows. An ordinary routing health check does not prove the continuity policy is active. # Clustering Source: https://docs.lasso.sh/deployment/clustering Configure named release nodes and private discovery for aggregated observability. Clustering is optional. A single node works standalone. Clustering enables: * Dashboard aggregates metrics across all nodes * Per-region drill-down for provider performance comparison * Cluster health monitoring (node status, region discovery) Each node makes its own provider-selection decision from local measurements; there is no cross-node coordination in the request hot path. Peers can still supply recovery hints for local block protection, and optional global block publication has a separate shared-storage contract. See [block continuity](/deployment/block-continuity) before treating a multi-node deployment as one continuity domain. ### Requirements * A private DNS A record that resolves to every node's reachable IP address. * Named Erlang nodes in the form `lasso@`, with the same private cookie. * Connectivity between nodes on EPMD port 4369 and Erlang distribution ports. Keep these ports private; Docker HTTP port mappings alone do not provide it. * A unique, stable `LASSO_NODE_ID` for each instance. ### Configuration For each release/container instance, set: | Variable | Example | Meaning | | - | - | - | | `RELEASE_DISTRIBUTION` | `name` | Enable long node names | | `RELEASE_NODE` | `lasso@10.0.0.11` | This node's name, using its reachable private IP | | `RELEASE_COOKIE` | Shared secret | Same private value on every node | | `CLUSTER_DNS_QUERY` | `lasso.internal` | DNS A record containing all node IPs | | `CLUSTER_NODE_BASENAME` | `lasso` | Must match the name before `@` | | `LASSO_NODE_ID` | `us-east-1` | Unique observability identity | Replace the example IP and DNS name with your network's values. For a second node at `10.0.0.12`, use `RELEASE_NODE=lasso@10.0.0.12` and a different `LASSO_NODE_ID`; keep the cookie, DNS query, and basename the same. Supply matching profile YAML to every node. Reload each node after adding or editing profiles. In Compose, put these values in each instance's `.env` and recreate its container with `docker compose up -d --force-recreate --wait`. Nodes poll DNS every five seconds. The deployment network must allow direct access to the IPs in DNS. Check the running node's name and peers: ```bash theme={null} docker compose exec -T lasso /app/bin/lasso rpc 'IO.inspect({node(), Node.list()})' ``` Each node should list the other members. An empty peer list means the node has not joined; check names, cookie equality, DNS results, and private connectivity. Setting discovery variables alone does not name a VM launched with plain `mix phx.server`; the release settings above apply to release/container startup. ### Geo-Distributed Deployment For optimal performance, deploy one Lasso node per region and route application traffic to the nearest node (via GeoDNS, anycast, or your load balancer's geographic routing). Each node independently: * Measures latency to upstream providers from its region * Selects providers using its configured strategy and local observations * Maintains independent circuit breaker state The dashboard aggregates data across all nodes for unified observability with regional drill-down. *** # Docker deployment Source: https://docs.lasso.sh/deployment/docker Run, verify, configure, upgrade, and roll back the public RPC Core image. The example below installs the verified v0.5.0 container. See [release evidence](/releases-and-availability) for its image digest and native installation checks. The primary distribution is `ghcr.io/jaxernst/lasso-rpc`, with native Linux AMD64 and ARM64 images. Use the Compose attachment from the release you select. It requires Docker Compose and OpenSSL for generating local secrets: ```bash theme={null} mkdir lasso && cd lasso curl --fail --location https://github.com/jaxernst/lasso-rpc/releases/download/v0.5.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 curl --fail http://localhost:4000/rpc/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` Open [http://localhost:4000/dashboard](http://localhost:4000/dashboard). Preserve `.env` across restarts and keep it private; it contains the signing and Erlang distribution secrets. Compose passes its values into the container, including provider credentials you add. Changing `.env` requires container recreation with `docker compose up -d --force-recreate --wait`; reloading YAML does not change a running container's environment. Compose binds port 4000 to localhost, runs as UID/GID `10001:10001`, and keeps the root filesystem read-only. `/tmp` is temporary; the named `/data` volume retains profiles and benchmark history. `docker compose down` retains that volume; `docker compose down --volumes` deletes it. Set `LASSO_PORT` in `.env` to choose another local port and `LASSO_NODE_ID` to give the instance a stable identity. #### Verify or pin the image Completed container publication attaches `container-release.json`, native verification reports, and `container-verification.md` with its immutable image digest. Version tags are never replaced with different contents. For a digest-pinned installation, set this in `.env`, using the digest from the selected release: ```dotenv theme={null} LASSO_IMAGE=ghcr.io/jaxernst/lasso-rpc@sha256:DIGEST ``` Then run `docker compose pull` and `docker compose up -d --wait`. To verify the signed publication attestation, install the GitHub CLI and run: ```bash theme={null} gh attestation verify oci://ghcr.io/jaxernst/lasso-rpc@sha256:DIGEST \ --repo jaxernst/lasso-rpc ``` The signed attestation identifies the publication workflow. Platform BuildKit provenance records the pinned public source used for the build; the index also contains SBOMs. Source and publication-tooling revisions are recorded separately in `container-release.json`. See [Releasing](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/RELEASING.md) for the verification scope, first-publication access setup, and retry behavior. #### Custom profiles and credentials An empty data volume is seeded with bundled profiles in `/data/config/profiles`. Existing files are preserved on restart and upgrade. Benchmark snapshots use `/data/benchmark_snapshots`. To maintain profiles on the host, copy the starting configuration from the running container: ```bash theme={null} mkdir profiles docker compose cp lasso:/data/config/profiles/. ./profiles ``` Create `compose.override.yml` next to `compose.yml`: ```yaml theme={null} services: lasso: environment: LASSO_PROFILES_DIR: /profiles volumes: - type: bind source: ./profiles target: /profiles read_only: true ``` Edit the YAML in `profiles/`, retaining a valid `public.yml`. Additional profiles need matching filenames and frontmatter slugs. See [Configuration](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/CONFIGURATION.md) for supported settings. Files must be readable by UID 10001 and directories traversable by that UID; host directories used for writable history must also be writable by UID 10001. Keep credential values in `.env` and reference them as `${VARIABLE_NAME}` in provider URLs or headers. Apply mount changes or environment changes: ```bash theme={null} docker compose up -d --force-recreate --wait ``` In v0.5.0, new profiles can reuse connected WebSocket upstreams after reload. If you remain on v0.3.4, recreate the container after adding profiles to avoid unavailable subscriptions. For new profiles and YAML-only edits in v0.5.0, reload the running node: ```bash theme={null} docker compose exec -T lasso /app/bin/lasso rpc 'IO.inspect(Lasso.Config.ConfigStore.reload())' ``` A successful reload prints `:ok`. A rejected reload reports its error and keeps the active configuration. Fix the file before restarting: a cold start cannot recover the prior in-memory configuration. Invalid files are logged during startup retries and prevent the service becoming ready. Use `docker compose logs lasso` to see the specific rejected field. A read-only mount supports loading and reloading; application-side configuration saves need a writable mount. `LASSO_PROFILES_DIR` overrides profile seeding and selection. `LASSO_SNAPSHOTS_DIR` independently overrides the history directory at runtime. #### Upgrade and rollback 1. Preserve `.env` and back up profiles and any history you need. For the default volume layout, `docker compose cp lasso:/data/config/profiles ./profiles-backup` and `docker compose cp lasso:/data/benchmark_snapshots ./history-backup` copy them out of the running container. 2. Read the target release's compatibility notes. Select its exact image tag or digest using `LASSO_IMAGE` in `.env`, and retain the previous value for rollback. 3. Run `docker compose pull`, then `docker compose up -d --wait`. Check health, an upstream-backed RPC request, and the dashboard. 4. Existing volume profiles are not replaced by newer bundled defaults. Compare provider changes with the target release's example profiles and merge them deliberately. YAML validation rejects unknown settings; check custom profiles before an upgrade rather than relying on formerly ignored options. 5. To roll back, restore the previous `LASSO_IMAGE`, pull, and recreate the container with the same environment. If the release changed the storage format, restore the matching backup according to its compatibility notes. Do not run `down --volumes` as an upgrade step. When migrating the v0.3.3 example profiles to v0.3.4, remove the ignored provider fields `type` and `api_key_required`. Replace the old `adapter_config.max_block_range` with `capabilities.limits.max_block_range`. Preserve your own provider URLs, credentials, and supported settings. The target release's bundled profiles show the supported shape. Images built before the persistent `/data` layout used `/app/config/profiles` and `/app/priv/benchmark_snapshots`. Copy customized configuration and history out of the old container before replacing it; a new empty volume cannot recover files from a deleted container. Restore the profiles into a readable host mount and the history into the new data volume, with ownership suitable for UID/GID `10001:10001`. Keep the old container/data and backups until the new deployment passes verification. #### Building locally The source repository's `compose.yml` builds from its checkout. From the selected Git tag, set `SECRET_KEY_BASE` and run `docker compose up --build -d`. Its local image is `lasso-rpc:local`; the downloadable Compose attachment uses the registry image instead. The foreground helper `./run-docker.sh` also builds locally and persists data in its own `lasso-rpc-data` volume. These source-build paths are useful for development or independently rebuilding a release. *** # Geographic deployment Source: https://docs.lasso.sh/deployment/geo-distributed Route clients to nearby Lasso nodes and inspect regional upstream performance. ### Geo-Distributed Deployment For optimal performance, deploy one Lasso node per region and route application traffic to the nearest node (via GeoDNS, anycast, or your load balancer's geographic routing). Each node independently: * Measures latency to upstream providers from its region * Selects providers using its configured strategy and local observations * Maintains independent circuit breaker state The dashboard aggregates data across all nodes for unified observability with regional drill-down. *** See [Clustering](/deployment/clustering) for the complete naming, discovery, and connectivity requirements. Upstream quotas can still be shared across regions; adding Lasso nodes does not increase a provider account quota. # Production checklist Source: https://docs.lasso.sh/deployment/production-checklist Prepare a self-hosted RPC Core deployment and verify its external boundary. ## Production Deployment Lasso is a standard Elixir/Phoenix release. It runs anywhere you can deploy an OTP release: containers, VMs, bare metal, or PaaS platforms. ### Building a Release ```bash theme={null} MIX_ENV=prod mix deps.get MIX_ENV=prod mix assets.setup MIX_ENV=prod mix assets.deploy MIX_ENV=prod mix release ``` Or build a Docker image using the included `Dockerfile`. ### Production environment variables | Variable | Description | | - | - | | `SECRET_KEY_BASE` | Phoenix signing/encryption secret. Generate with `mix phx.gen.secret` (64+ bytes) | | `PHX_HOST` | Set your public hostname for URL generation (e.g., `rpc.example.com`); defaults to `localhost` | | `PHX_SERVER` | Optional. Production already starts the HTTP server; the release `bin/server` wrapper sets this explicitly | | `LASSO_NODE_ID` | Unique, stable identifier for this node. Required in production. Convention: use region names (e.g., `us-east-1`) for geo-distributed deployments | | `PORT` | HTTP listener port (default: `4000`) | ### Provider API Keys Provider URLs in profile YAML support `${ENV_VAR}` substitution. Unresolved placeholders crash at startup. ```yaml theme={null} providers: - id: "alchemy_ethereum" url: "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}" ``` Set the variable in your environment or secrets manager: ```bash theme={null} export ALCHEMY_API_KEY="your-key-here" ``` ### Health Check Core v0.5.0 exposes `GET /api/health` for application liveness and `GET /api/ready` for route readiness. Configure your orchestrator to use health for process availability. Scope readiness with `profile` and `chain` when a load balancer needs to assess one application route. The health endpoint confirms that the application is running and reports cluster topology. Readiness uses local eligible HTTP routes and fresh head observations; it does not make a live upstream request. Pair both endpoints with an upstream-backed RPC check for your actual method and provider pool. ### HTTPS Lasso serves HTTP. Terminate TLS at your reverse proxy or load balancer. Set `PHX_HOST` to your public hostname. Production URL generation defaults to HTTPS; set `PHX_SCHEME=http` when serving locally without a TLS proxy. *** ## Production Checklist * [ ] `SECRET_KEY_BASE` set (64+ bytes) * [ ] `PHX_HOST` set to public hostname * [ ] HTTP server responds to `GET /api/health` * [ ] Intended profile and chain respond to `GET /api/ready?profile=...&chain=...` after head observation warms * [ ] `LASSO_NODE_ID` set to a unique, stable value * [ ] Provider credentials set when referenced by profile configuration * [ ] Health check (`GET /api/health`) monitored by orchestrator * [ ] Profile YAML validated with `bin/lasso check-config` before `bin/lasso reload`; unresolved `${ENV_VAR}` prevents readiness at cold start * [ ] Client request limits enforced at the reverse proxy; profile rate settings only configure the dashboard tester * [ ] TLS terminated at reverse proxy / load balancer * [ ] Console log drain configured; set `LOG_FORMAT=json` if your collector requires JSON * [ ] Each node's `GET /metrics` scrape restricted to the collector and labeled by instance * [ ] RPC and dashboard protected by reverse-proxy authentication or a private network boundary (Lasso OSS has no built-in client authentication) * [ ] If clustering: named nodes, shared cookie, DNS discovery, and private distribution connectivity configured; peers visible in `Node.list()` Verify an upstream-backed RPC and a complete subscribe/event/unsubscribe flow after deployment. Preserve the previous image and configuration for rollback. # Route across your own RPC providers Source: https://docs.lasso.sh/guides/route-across-providers Run Lasso RPC Core in front of your Ethereum, Base, and Arbitrum providers, then inspect and test routing. Lasso RPC Core is an open-source EVM JSON-RPC proxy for a provider pool you control. It selects eligible upstreams from live health and recent request evidence. Replay-safe reads can fail over within the request deadline and dispatch budget. Provider quotas and method coverage still limit the pool. For a hosted pool, use [Lasso Cloud custom profiles](/cloud/bring-your-own-rpc). This guide runs the [released Core container](/deployment/docker) locally. ## Start the container Install the selected release using the commands in [Docker deployment](/deployment/docker). Confirm `http://localhost:4000/api/health` responds, then make an upstream-backed request from the [quickstart](/quickstart). The health check alone does not prove that an RPC provider is reachable. ## Add your providers Follow [Docker deployment: custom profiles and credentials](/deployment/docker#custom-profiles-and-credentials) to mount a `profiles/` directory. Keep its valid `public.yml`. Save the following as `profiles/my-app.yml`. Replace every environment variable with a credential-bearing URL from a provider or node you control. Each chain needs providers that actually serve that chain. ```yaml theme={null} --- name: My App slug: my-app --- chains: ethereum: chain_id: 1 providers: - id: eth_a url: ${ETH_RPC_A} - id: eth_b url: ${ETH_RPC_B} base: chain_id: 8453 providers: - id: base_a url: ${BASE_RPC_A} - id: base_b url: ${BASE_RPC_B} arbitrum: chain_id: 42161 providers: - id: arb_a url: ${ARB_RPC_A} - id: arb_b url: ${ARB_RPC_B} ``` Store the variables in the private `.env` file used by Compose. Do not commit provider credentials. Unresolved variables reject the configuration. Validate chain identity and the methods your app needs against each upstream; a second configured URL does not prove an independent failure domain. See [provider configuration](/configuration/providers) for declared capabilities and [provider selection](/concepts/provider-selection) for eligibility. After adding the mounted file or changing `.env`, recreate the container: ```bash theme={null} docker compose up -d --force-recreate --wait ``` For later YAML-only edits, use the [configuration reload](/deployment/docker#custom-profiles-and-credentials). Check its result before shifting application traffic. ## Exercise each route Send an upstream-backed read through each chain. In the released Core version, `fastest` uses qualified recent latency evidence from client requests across methods for each provider and transport; the default route is `load-balanced`. Neither strategy promises that one provider always wins. ```bash theme={null} for chain in ethereum base arbitrum; do curl --fail-with-body --silent --show-error \ "http://localhost:4000/rpc/profile/my-app/fastest/$chain?include_meta=body" \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' done ``` Check the response `result` and `lasso_meta` for each chain. The metadata identifies selection candidates, recorded attempts, and the executed channel when available. A candidate is not proof of a dispatch. See [request metadata](/observability/request-metadata) for the released field contract and a failover example. ## Test a provider failure In a non-production trial, interrupt one upstream that you control while you send replay-safe reads through the profile. Compare successful responses, errors, p95/p99 latency, head freshness, and `attempted_channels` before and during the fault. Then restore it and check recovery. Lasso can try another eligible provider within its deadline and dispatch budget; it cannot recover if the remaining pool is unavailable, over quota, or does not support the request. Signed transactions and other unsafe work have different replay rules in the [method contract](/api/supported-methods). Keep the original provider route available for rollback while you compare the same application traffic. Before exposing the container to clients, apply the [network and authentication guidance](/deployment/production-checklist): Core does not provide inbound API-key authentication or per-client RPC quotas. # Installation Source: https://docs.lasso.sh/installation Install Lasso RPC Core from the verified container release or source. Lasso RPC Core v0.5.0 runs on your infrastructure. For a managed endpoint, use the [Lasso Cloud quickstart](/cloud/quickstart). ## Container installation See [release evidence](/releases-and-availability) for the verified v0.5.0 image digest and native installation checks. The primary distribution is `ghcr.io/jaxernst/lasso-rpc`, with native Linux AMD64 and ARM64 images. Use the Compose attachment from the release you select. It requires Docker Compose and OpenSSL for generating local secrets: ```bash theme={null} mkdir lasso && cd lasso curl --fail --location https://github.com/jaxernst/lasso-rpc/releases/download/v0.5.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 curl --fail http://localhost:4000/rpc/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` Open [http://localhost:4000/dashboard](http://localhost:4000/dashboard). Preserve `.env` across restarts and keep it private; it contains the signing and Erlang distribution secrets. Compose passes its values into the container, including provider credentials you add. Changing `.env` requires container recreation with `docker compose up -d --force-recreate --wait`; reloading YAML does not change a running container's environment. Compose binds port 4000 to localhost, runs as UID/GID `10001:10001`, and keeps the root filesystem read-only. `/tmp` is temporary; the named `/data` volume retains profiles and benchmark history. `docker compose down` retains that volume; `docker compose down --volumes` deletes it. Set `LASSO_PORT` in `.env` to choose another local port and `LASSO_NODE_ID` to give the instance a stable identity. ## Source installation ### Prerequisites * **Elixir**: 1.18.4 (CI version) * **Erlang/OTP**: 28 (CI version) * **Node.js**: 18+ (asset compilation) ### Setup ```bash theme={null} git clone https://github.com/jaxernst/lasso-rpc cd lasso-rpc git checkout v0.5.0 mix deps.get mix assets.setup mix assets.build mix phx.server ``` Available at `http://localhost:4000`. Dashboard at `http://localhost:4000/dashboard`. The included `public` profile includes free public providers — no API keys required. ### `.env` File Lasso loads a `.env` file from the project root if present (via Dotenvy). System environment variables take precedence over `.env` values. ```bash theme={null} # .env LASSO_NODE_ID=local-dev ALCHEMY_API_KEY=your-key-here ``` ## Configure and verify Keep `config/profiles/public.yml`; routes without a profile use `public`. Public upstreams require no provider key, but have their own availability and quotas. A successful health response proves the application is running; make an upstream-backed RPC request as well. For custom profiles, persistent storage, image verification, upgrades, and rollback, see [Docker deployment](/deployment/docker). RPC Core has no built-in client authentication or incoming RPC quotas. Protect externally accessible RPC, metrics, and dashboard endpoints at your network or reverse proxy. # Introduction Source: https://docs.lasso.sh/introduction Self-hosted Ethereum RPC routing with provider controls and observability. Lasso RPC Core routes Ethereum JSON-RPC across your nodes and providers over HTTP and WebSocket. Configure a provider pool, choose a routing strategy, and inspect performance in the dashboard. Use **RPC Core** to run the open-source engine on your infrastructure. Use [Lasso Cloud](/cloud/overview) for managed endpoints, accounts, API keys, and billing. These sections describe RPC Core v0.5.0; Cloud has a separate [compatibility contract](/cloud/json-rpc-compatibility). See [Versions, availability, and evidence](/releases-and-availability) to compare the released Core artifact with the separately deployed Cloud service. [Support, security, incidents, and lifecycle](/support-security-lifecycle) explains the public help path, supported-version policy, and repository boundaries. ## Routing and resilience * Select providers using measured latency, load balancing, configured priority, or a direct provider route. * Apply transport health, circuit state, declared method restrictions, and request limits when selecting candidates. * Bound retries by execution safety and the original request deadline. Signed transaction submission gets one upstream dispatch. * Share matching `newHeads` and `logs` subscriptions and recover within explicit replay and time limits. * Inspect local and aggregated observations with optional clustering. Each node makes its own routing decisions. Provider plans, quotas, history, and parameter support still constrain what Lasso can serve. More profiles or Lasso nodes do not multiply an upstream account's quota. ## Get started [Install the public container](/installation), [make your first request](/quickstart), then [configure your own providers](/configuration/providers). The included `public.yml` profile lets you try public upstreams without provider keys. RPC Core has no built-in client authentication or incoming customer quotas. Protect exposed RPC, metrics, and dashboard endpoints at your network or reverse proxy. See [Production checklist](/deployment/production-checklist). # Operator dashboard Source: https://docs.lasso.sh/observability/dashboard Inspect Core's live provider, routing, and system observations. Open `http://localhost:4000/dashboard` for the `public` profile, or `/dashboard/{profile}` for another configured file profile. Protect the dashboard with a private network boundary or reverse-proxy authentication when reachable by untrusted clients; RPC Core has no built-in client authentication. The dashboard shows provider connectivity, circuit state, block freshness, recent routing activity, and a browser request tester. The tester makes real upstream requests. Its counters describe that browser's run, while provider measurements describe observed upstream attempts. A single client request may produce multiple attempts during failover. Missing observations should be read as unavailable, not zero. In a cluster, the dashboard can aggregate observations from responding nodes and display coverage. Each node still makes its own routing decisions using its local measurements. A partial or stale aggregate is not proof that every node has the same provider view. See [clustering](/deployment/clustering) for discovery requirements and [metrics and telemetry](/observability/metrics) for measurement units. System metrics are disabled by default; set `LASSO_VM_METRICS_ENABLED=true` to collect and display VM metrics. For programmatic node-local measurements use [the metrics API](/api/metrics-api). For implementation details see the [released observability reference](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/OBSERVABILITY.md). # Operational logging Source: https://docs.lasso.sh/observability/logging Core's request telemetry, event logs, and log-format configuration. RPC Core emits operational logs for failover, provider exhaustion, slow requests, and circuit breaker transitions. Production enables `Lasso.TelemetryLogger`; its event categories can be enabled or disabled through application configuration. Text remains the default console format. Set `LOG_LEVEL=debug|info|warning|error` to control the level and `LOG_FORMAT=json` for one JSON object per line. JSON entries include UTC time, severity, message, and an allowlist of route and request identifiers. The formatter masks embedded upstream URL paths and queries and common labeled credential assignments. Do not put secrets in arbitrary freeform messages or provider identifiers. Protect the log drain at your deployment boundary. ```elixir theme={null} config :lasso, Lasso.TelemetryLogger, enabled: true, log_slow_requests: true, log_failovers: true, log_circuit_breaker: true ``` Core does not emit a JSON `rpc.request.completed` log for every request, and it has no request-log sampling configuration. If you need a request-completion stream, attach a short handler to `[:lasso, :rpc, :request, :stop]`. Its `duration` measurement is in milliseconds; metadata includes chain, method, strategy, provider, transport, result, and failover count. This event describes routed completion, not every ingress request. Keep telemetry handlers nonblocking because they run in the emitting process. For client correlation, opt into [response metadata](/observability/request-metadata). Provider identifiers can appear in logs and metadata, so do not put secrets in them. Protect logs using your deployment's normal access controls. See the [released observability reference](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/OBSERVABILITY.md) for the event contract and [TelemetryLogger source](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/observability/telemetry_logger.ex) for its handlers. # Metrics and telemetry Source: https://docs.lasso.sh/observability/metrics Read Core's node-local metrics and distinguish request events from upstream attempts. RPC Core's dashboard shows provider connectivity, circuit state, block freshness, upstream attempt rates, success rates, and latency distributions. These observations belong to a node and its available observation window. An unavailable value is not a measured zero. `GET /api/metrics/:chain` returns JSON metrics for the node-local `public` profile. Its provider and method measurements describe upstream attempts; a client request can make several attempts during failover. See the [metrics API](/api/metrics-api) for its fields, units, and nullable values. `GET /metrics` exposes Prometheus text for node-local routed request counts by chain, provider, method, and outcome. It also reports local circuit state and fresh provider head lag. Request labels occupy at most 4,096 series slots; unknown methods use `other`, and dropped observations increment `lasso_rpc_request_observations_dropped_total`. Circuit and lag samples scan at most 2,048 configured routes per scrape. Missing lag is unknown, not zero. Scrape each Core node separately and retain an instance label in Prometheus when you aggregate nodes. Restrict `/metrics` to your collector at the proxy or private network boundary: Core does not authenticate that endpoint, and labels can include configured provider identifiers. You can import the [versioned Grafana dashboard](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/grafana/lasso-core-v1.json) to inspect these series. The dashboard request tester generates real upstream traffic. Its success, error, and latency counters describe that browser's test run. Open WebSocket connection counts do not establish that every subscription succeeded; the activity feed records confirmations and errors separately. The request pipeline also emits `[:lasso, :rpc, :request, :stop]` with a millisecond `duration` measurement and metadata for chain, method, strategy, provider, transport, result, and failovers. This event describes routed request completion. Locally handled methods, invalid requests, and pre-dispatch failures follow other paths, so it is not a complete ingress counter. An embedding deployment can attach its own telemetry handler and export measurements to its monitoring system. VM/system metrics are disabled by default. Set `LASSO_VM_METRICS_ENABLED=true` to collect and display them. Protect metrics and dashboard routes at your ingress when deployed publicly. See the [released observability reference](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/docs/OBSERVABILITY.md) for the event schema and [metrics API](/api/metrics-api) for response examples. # Request metadata Source: https://docs.lasso.sh/observability/request-metadata Released Core v0.5.0 routing fields and reproducible response examples This page describes **Lasso RPC Core v0.5.0** metadata. It is opt-in and describes request execution, not an upstream-capacity or availability guarantee. [Cloud RPC behavior](/cloud/rpc-behavior) and [versions and evidence](/releases-and-availability) separate managed-service fields and deployment state from this Core contract. ## Request metadata Choose `include_meta=body` or `include_meta=headers`. The equivalent request header is `X-Lasso-Include-Meta: body` or `headers`; the query parameter takes precedence. Omit both to keep the ordinary response format. ```bash theme={null} curl 'http://localhost:4000/rpc/ethereum?include_meta=body' \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` ## Metadata fields The released [metadata builder](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/observability/observability.ex) removes **top-level nil values**. Nested attempt fields can still be `null`. Treat missing optional evidence as unknown, and tolerate additional fields. | Field | Meaning | | - | - | | `version` | Metadata schema version, currently `"1.0"`. | | `request_id` | Opaque bounded correlation string, not a UUID-v4 contract. | | `chain_id` | Numeric chain identity, not a chain-name `chain` field. | | `profile_id`, `service_profile_id` | Effective and service file-profile identities; Core uses the same profile value for both. | | `strategy` | Strategy name, such as `priority`, `fastest` or `load_balanced`. | | `transport` | Request transport. The selected/executed channel identifies the upstream transport. | | `candidate_providers` | Candidate labels considered by selection; not a dispatch log. | | `selected_provider` | Optional selected provider identity (`id`) and `protocol`; no guaranteed name or region. | | `executed_channel` | Optional channel that successfully executed the request. | | `attempted_channels` | Ordered recorded success/error attempts, with channel identity and error category/code where present. | | `selection_latency_ms` | Optional selection timing. | | `upstream_latency_ms` | Optional measured upstream I/O timing. | | `end_to_end_latency_ms` | Optional end-to-end timing; not `total_latency_ms`. | | `lasso_overhead_ms` | Optional computed internal overhead. | | `retries` | Pipeline retry counter; not a count of candidate providers or proof of exact dispatch count. | | `circuit_breaker_state` | Reported circuit state, or `unknown`. | | `head_policy` | Optional [block protection evidence](#block-protection-metadata). | A channel identity contains `profile`, `provider_id`, `instance_id`, `transport` and `route_generation`. Instance/profile identifiers are opaque. The recorded attempt contains `channel`, `outcome`, `category` and `code`; successful attempts can have null category/code. See the released [request context](https://github.com/jaxernst/lasso-rpc/blob/v0.5.0/lib/lasso/core/request/request_context.ex). Core generates an opaque request ID when needed; its fallback is 32 lowercase hexadecimal characters. Do not validate all IDs against that fallback shape or confuse this correlation ID with the JSON-RPC request's `id`. ## Body mode `include_meta=body` adds `lasso_meta` beside `result` or `error`. Read the raw HTTP JSON response when you need this outer field: SDK methods commonly return only `result` and discard the envelope. These examples were generated with the v0.4.3 metadata builder and channel-history helpers, which are unchanged in v0.5.0. Provider identities and timings are fixed hermetic fixture inputs, not live performance measurements. The fixtures follow the released [success and failover test setup](https://github.com/jaxernst/lasso-rpc/blob/v0.4.3/test/integration/request_pipeline_integration_test.exs). ### Successful first attempt ```json theme={null} { "id": 1, "jsonrpc": "2.0", "lasso_meta": { "version": "1.0", "strategy": "priority", "chain_id": 1, "transport": "http", "request_id": "fixture-request", "candidate_providers": [ "primary:http", "backup:http" ], "circuit_breaker_state": "closed", "selection_latency_ms": 1.0, "upstream_latency_ms": 20.0, "end_to_end_latency_ms": 23.0, "lasso_overhead_ms": 3.0, "retries": 0, "selected_provider": { "id": "primary", "protocol": "http" }, "attempted_channels": [ { "code": null, "category": null, "channel": { "profile": "public", "provider_id": "primary", "instance_id": "fixture-primary", "transport": "http", "route_generation": 7 }, "outcome": "success" } ], "executed_channel": { "profile": "public", "provider_id": "primary", "instance_id": "fixture-primary", "transport": "http", "route_generation": 7 }, "profile_id": "public", "service_profile_id": "public" }, "result": "0x64" } ``` ### Successful failover The failed primary and successful backup are both in `attempted_channels`. `executed_channel` identifies the backup. A candidate label alone would not establish either attempt. ```json theme={null} { "id": 1, "jsonrpc": "2.0", "lasso_meta": { "version": "1.0", "strategy": "priority", "chain_id": 1, "transport": "http", "request_id": "fixture-request", "candidate_providers": [ "primary:http", "backup:http" ], "circuit_breaker_state": "closed", "selection_latency_ms": 1.0, "upstream_latency_ms": 20.0, "end_to_end_latency_ms": 23.0, "lasso_overhead_ms": 3.0, "retries": 1, "selected_provider": { "id": "backup", "protocol": "http" }, "attempted_channels": [ { "code": -32000, "category": "server_error", "channel": { "profile": "public", "provider_id": "primary", "instance_id": "fixture-primary", "transport": "http", "route_generation": 7 }, "outcome": "error" }, { "code": null, "category": null, "channel": { "profile": "public", "provider_id": "backup", "instance_id": "fixture-backup", "transport": "http", "route_generation": 7 }, "outcome": "success" } ], "executed_channel": { "profile": "public", "provider_id": "backup", "instance_id": "fixture-backup", "transport": "http", "route_generation": 7 }, "profile_id": "public", "service_profile_id": "public" }, "result": "0x64" } ``` ## Headers mode `include_meta=headers` puts the same metadata object in `X-Lasso-Meta` as base64url-encoded JSON without padding. `X-Lasso-Request-ID` is the correlation ID. There is no promised `X-Lasso-Latency` header. Decode the header and handle its absence explicitly: ```javascript theme={null} const encoded = response.headers.get('x-lasso-meta'); let metadata = null; if (encoded) { const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/'); const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '='); const bytes = Uint8Array.from(atob(padded), char => char.charCodeAt(0)); metadata = JSON.parse(new TextDecoder().decode(bytes)); } console.log({requestId: response.headers.get('x-lasso-request-id'), metadata}); ``` If the encoded metadata exceeds `max_meta_header_bytes` (default 4,096 bytes), Core omits `X-Lasso-Meta` and retains `X-Lasso-Request-ID`. Body mode avoids this metadata-header limit; it is not a promise of unlimited response size. ```elixir theme={null} config :lasso, :observability, max_meta_header_bytes: 4096 ``` ## Record evidence safely Store the correlation ID, result/error, source revision and available timing and channel fields. Record missing metadata explicitly. A successful request proves neither independent operator redundancy nor that every candidate was attempted. Do not use `candidate_providers` as an upstream amplification counter. ## Block protection metadata These optional fields help diagnose [Block regression protection](/advanced/block-continuity). They are not required to enable protection or read several values at one block. When `head_policy.policy` is `local`, a protected latest-block response can include: | Field in `head_policy` | Meaning | | - | - | | `block_number`, `block_hash`, `block_age_ms` | The accepted block and its age. | | `instance`, `generation` | The serving Lasso instance and application lifetime. A replacement application starts a new generation. | | `minimum_height` | The local floor captured when the request started. | | `recovery_height` | A best-effort recovery height hint, or `null` if none was available. | | `recovery_gap_blocks` | The gap below the recovered height. A nonzero gap is allowed; hints are not mandatory minimums. | Compare nonoverlapping requests within the same service profile, chain, instance and generation to assess local protection. These fields do not prove that a particular number of peers received an update or predict recovery time. Global policies report `policy=global` and can include attributed chain-change observations. Their retained number/header responses have no executing upstream provider. Explicit-number/hash requests can legitimately have no `head_policy` object. State-read results do not independently attest which block the provider used to execute them. For correlation, use `x-lasso-request-id`, or `x-request-id` when no routing context was created. `service_profile_id` identifies the service profile and `profile_id` identifies the effective route. Core currently uses the same file-profile identity for both fields; treat them as opaque values. HTTP batch headers expose only one item's routing context. Oversized metadata can be omitted. If complete per-request diagnostics are required, use individual HTTP requests and record missing metadata explicitly; this is a diagnostics choice, not a requirement for consistent reads. # Quickstart Source: https://docs.lasso.sh/quickstart Start the released RPC Core container and verify HTTP and WebSocket routing. ## Start Lasso RPC Core Follow [Installation](/installation) to start the v0.5.0 container or run from source. The included `public` profile is ready to try without provider keys. ## Make your first requests Request the latest Ethereum block number: ```bash theme={null} curl --fail-with-body --silent --show-error http://localhost:4000/rpc/ethereum \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` A successful response contains a hexadecimal block number; the value changes: ```json theme={null} {"jsonrpc":"2.0","id":1,"result":"0x18ba3fb"} ``` The health endpoint checks the application. This RPC request also exercises an upstream provider. Public providers have their own availability and rate limits. For WebSocket subscriptions, use the dashboard tester or run [`wscat`](https://github.com/websockets/wscat) with Node.js and npm: ```bash theme={null} npx --yes wscat -c ws://localhost:4000/ws/rpc/ethereum ``` At its `>` prompt, send: ```json theme={null} {"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1} ``` Expect a subscription ID followed by `eth_subscription` notifications as blocks arrive. Press Ctrl+C to disconnect. To inspect routing decisions, add `?include_meta=headers` to an HTTP request URL and `-i` to curl. See the [API reference](/observability/request-metadata) for metadata fields and error responses. ## Choose a route Use `/rpc/fastest/ethereum`, `/rpc/load-balanced/ethereum`, or `/rpc/latency-weighted/ethereum` for explicit strategies. Use `/rpc/profile/my-app/ethereum` for a configured profile named `my-app`. WebSocket routes use the `/ws/rpc/` prefix. Provider health, method support, and configured limits constrain selection. Routing cannot create upstream capacity or guarantee a provider supports every parameter combination. See [Configuration](/configuration/overview), [HTTP endpoints](/api/http-endpoints), and [Docker deployment](/deployment/docker). # Versions, availability, and evidence Source: https://docs.lasso.sh/releases-and-availability Choose between released RPC Core and managed Cloud, with versioned proof and workload limits. Use **Lasso RPC Core** when you want to operate the routing engine and its network boundary. Use **Lasso Cloud** when you want managed routing, accounts, keys, billing, and provider operations. A shared engine does not mean that a Core release and the hosted service have identical behavior. ## Version reference Checked September 28, 2026: | Surface | Version or observation | What it establishes | | - | - | - | | RPC Core source | [v0.5.0](https://github.com/jaxernst/lasso-rpc/releases/tag/v0.5.0), source `e98587736dfd2ebcf4cd155518f2b7f37059b4dc` | The released source described by the Core API guides. [Exact-main CI](https://github.com/jaxernst/lasso-rpc/actions/runs/36428798899) passed; container publication is a separate gate. | | RPC Core container | v0.5.0, image index `sha256:b77c50c66a4e2d991135e96811e0d6e898208b2df83237853424636bd9b4716e` | The [release manifest](https://github.com/jaxernst/lasso-rpc/releases/download/v0.5.0/container-release.json) binds source and image digests. Native AMD64 and ARM64 installation checks passed before [publication completed](https://github.com/jaxernst/lasso-rpc/actions/runs/36430522538). | | Managed Cloud | Public health reported source `62bda27e2756507b1ca89b43396fe178c7308816` in four regions at `2026-09-28T18:15:39Z` | The production rollout passed exact-fleet attestation. Recheck [health](https://lasso.sh/api/health) and [service discovery](https://lasso.sh/agent.json) for your evaluation. Process health does not qualify your workload. | | Cloud APIs and payment availability | Live [OpenAPI](https://lasso.sh/openapi.json), [manifest](https://lasso.sh/agent.json), and [deployment guide](https://lasso.sh/SKILL.md) | Available routes, credential classes, and enabled payment rails on that deployment. An implemented API can still be disabled. | ## Workload and ownership matrix “Supported” here identifies an interface and its documented boundary. It does not certify every provider pool, chain, request parameter, or sustained load. | Capability | RPC Core v0.5.0 source | Managed Cloud | Qualification boundary | | - | - | - | - | | HTTP reads and batches | Supported; you configure and operate upstreams. | Managed or account-owned provider pools. | History, method support, quotas, and parameters depend on eligible upstreams. Batches route items independently and do not pin a snapshot. | | Signed transactions | One upstream dispatch for `eth_sendRawTransaction`. | Conservative single dispatch. | An acknowledgment is not inclusion or finality. Reconcile an indeterminate result by transaction hash. | | WebSocket unary requests, `newHeads`, and `logs` | Supported; enable intended providers and protect your endpoint. | Account-key WebSocket access is advertised. Anonymous-key WebSocket access is not. | Recovery is bounded. Reconnect and reconcile application checkpoints after termination. Pending-transaction subscriptions are outside the released subset. | | Historical state and log ranges | Forwarded subject to provider capabilities. | Provider capability and availability constrain routing. | No universal archive depth or log-range guarantee. Qualify your exact historical witnesses. | | Stateful filters | Can forward when capability policy permits, without cross-request upstream affinity. | Rejects the documented [HTTP filter lifecycle](/cloud/rpc-behavior#stateful-http-filters) locally with `-32601`. | Use `eth_getLogs` or supported subscriptions; filter IDs are provider-local. | | Block regression protection | Opt-in `local` policy with best-effort peer recovery; optional coordinated policy requires its own operating setup. | Custom-profile On/Off control for local protection. | Local protection covers sequential choices within one running application generation. A server switch or restart can return a lower block. | | Hash-selected state reads | Explicit selectors are retained through routing and retries. | Same documented application pattern. | Choose and carry one hash through every read; upstream state and selector support are required. This is not finality or execution proof. | | Identity, billing, incoming customer quotas | Outside Core. Protect exposed endpoints through your network or reverse proxy. | Account, key, entitlement, and usage services. | Profiles do not multiply an upstream account's quota. [Account and anonymous CU differ](/cloud/observability#usage-units). | | Prepaid funding | Outside Core. | Anyone can buy key credit through an enabled Tempo MPP or Base x402 402 exchange. An account-owned key can buy time-limited Custom access. | Check live rail flags, price and bounds in the [manifest](https://lasso.sh/agent.json); wallet authority is separate from Lasso credentials. See the [agent flow](/cloud/agent-flows). | | Sustained workload capacity | Depends on your deployment and providers. | Depends on workload, profile, providers, and service policy. | The linked evidence does not establish a general 24-hour, seven-day, or 30-day operating envelope across these workloads. | Use the detailed [Core method contract](/api/supported-methods), [Cloud workload behavior](/cloud/rpc-behavior), and [Cloud compatibility contract](/cloud/json-rpc-compatibility) for execution and failure semantics. Before adoption, evaluate one real application flow using [the two-provider guide](/cloud/bring-your-own-rpc), including its recovery path. ## Public release evidence The [v0.5.0 source release](https://github.com/jaxernst/lasso-rpc/releases/tag/v0.5.0) adds route readiness, node-local Prometheus metrics, JSON console logging, profile validation and reload commands, standalone provider discovery, and bounded routing evidence. It tightens HTTP/WebSocket resource limits, preserves send certainty across timed-out Finch checkouts, and moves stale subscription recovery through the continuity coordinator. Mint 1.11.0 and HPAX 1.1.0 include published HTTP framing and HTTP/2 fixes. Existing profiles and the optional publication journal require no schema migration; stricter request and discovery validation can reject malformed inputs accepted earlier. The source CI is not container installation proof; the native artifact has separate evidence below. The [v0.4.5 source release](https://github.com/jaxernst/lasso-rpc/releases/tag/v0.4.5) upgrades Phoenix, Cowboy, Cowlib and Ranch (Cowlib 2.20.0 resolves `CVE-2026-43971`) and corrects Public Arbitrum archive routing: thirdweb and Blast serve historical state, while dRPC, PublicNode, Tenderly and Nodies no longer claim it. Block-hash state reads now require a declared archival provider, and requests that no configured provider can serve fail at admission with a structured, non-retriable [`reason`](/api/error-codes#core-routing-exhaustion) instead of contacting an upstream. No configuration or journal migration is required; operators with local profile overrides should review their archival declarations against historical state reads. The [v0.4.4 source release](https://github.com/jaxernst/lasso-rpc/releases/tag/v0.4.4) gives distinct physical provider instances a first pass within each health tier for load-balanced, replay-safe unary fallback. Alternate transports remain available; health tiers and explicit recovered-head preference retain precedence. The dispatch budget, original deadline and candidate admission limits are unchanged. No configuration or journal migration is required. Trying another instance first can delay a working alternate transport. This does not certify archive coverage, operator independence or provider capacity; see the [full routing boundary](/concepts/routing-strategies#load-balanced). The [v0.4.3 source release](https://github.com/jaxernst/lasso-rpc/releases/tag/v0.4.3) rejects an HTTP endpoint after a malformed or mismatched chain-ID probe, orders buffered orphan-log events before replacement-block additions, and removes URL user information, paths, queries, and fragments from provider diagnostics. It requires no configuration or journal migration. These fixes do not qualify archive depth or arbitrary reorg recovery. See the [HTTP identity boundary](/api/supported-methods#reads-and-execution-safety) and [subscription recovery limits](/advanced/websocket-subscriptions#application-checkpoints-and-reorgs). The v0.5.0 [container verification summary](https://github.com/jaxernst/lasso-rpc/releases/download/v0.5.0/container-verification.md) records 23 controlled checks on each native [AMD64](https://github.com/jaxernst/lasso-rpc/releases/download/v0.5.0/verification-amd64.json) and [ARM64](https://github.com/jaxernst/lasso-rpc/releases/download/v0.5.0/verification-arm64.json) image. Both runners pulled the multi-platform index anonymously and checked installation, operator endpoints, authenticated proxy HTTP and WebSocket traffic, reload, container replacement, and optional journal continuity. The [manifest](https://github.com/jaxernst/lasso-rpc/releases/download/v0.5.0/container-release.json) records the immutable image digest. These controlled checks do not measure sustained capacity or qualify every provider workload. The v0.4.5 [container verification summary](https://github.com/jaxernst/lasso-rpc/releases/download/v0.4.5/container-verification.md) records 19 installation and recovery checks on each native [AMD64](https://github.com/jaxernst/lasso-rpc/releases/download/v0.4.5/verification-amd64.json) and [ARM64](https://github.com/jaxernst/lasso-rpc/releases/download/v0.4.5/verification-arm64.json) image. It covers installation, custom profiles, HTTP/WebSocket routing, container replacement, saved benchmark history, and optional journal recovery. The preceding [v0.4.4](https://github.com/jaxernst/lasso-rpc/releases/download/v0.4.4/container-verification.md), [v0.4.3](https://github.com/jaxernst/lasso-rpc/releases/download/v0.4.3/container-verification.md) and [v0.4.2 container reports](https://github.com/jaxernst/lasso-rpc/releases/download/v0.4.2/container-verification.md) remain historical evidence for those earlier images. The v0.4.2 [feature validation report](https://github.com/jaxernst/lasso-rpc/releases/download/v0.4.2/feature-validation.md) records controlled and managed-service block-choice and hash-read checks. It preserves failed quota and latency populations, cold-channel limitations, and cross-server backsteps alongside the final warmed-channel pass. Those bounded checks do not establish a customer-capacity estimate, a fleet-wide monotonicity guarantee, or an SLA. They do not separately qualify arbitrary log reorg recovery or your transaction lifecycle. ## Routing benchmark The published [routing-overhead report](https://lasso.sh/benchmarks/routing-overhead.html) measures added p95 latency of 0.70–1.08 ms across five short synthetic-provider trials at 10,000 requests per second. A separate 60-second instrumented run measured 2.29 ms. The [raw evidence ledger](https://lasso.sh/benchmarks/lasso-routing-overhead-ledger-2026-08-24.tar.gz) preserves accepted, rejected, and sustained runs. This is a routing-engine measurement. It excludes hosted authentication, database metering, Internet/provider latency, multiple regions, and WebSockets. Use client-observed latency, correctness, recovery, and upstream volume from your own workload when deciding whether to adopt Lasso. # Support, security, incidents, and lifecycle Source: https://docs.lasso.sh/support-security-lifecycle Public support paths, security reporting, incident expectations, and version boundaries for Lasso Cloud and RPC Core. Use the channel that matches the surface and the sensitivity of the report. Lasso is an early product and project. Public support is currently best-effort. There is no general response-time, restoration-time, uptime, or advance-notice commitment unless you have a separate written agreement. ## Get help or report a problem | Need | Public path | Include | | - | - | - | | Lasso Cloud product question or incident | [Open a Lasso documentation issue](https://github.com/jaxernst/lasso-docs/issues/new) | UTC time, affected route shape without credentials, chain, RPC method, response code, request ID, and whether retry or rollback restored service | | RPC Core bug or feature request | [Open an RPC Core issue](https://github.com/jaxernst/lasso-rpc/issues/new) | Core version or commit, deployment shape, minimal configuration with secrets removed, reproduction, expected result, and actual result | | Documentation problem | [Open a Lasso documentation issue](https://github.com/jaxernst/lasso-docs/issues/new) | Page URL, the unclear or incorrect text, and the behavior you observed | | Security vulnerability | Follow the private reporting instructions below | Affected surface, reproduction, likely impact, and suggested mitigation if available | GitHub issues are an asynchronous support channel. They are not an emergency pager. Do not post Lasso keys, cookies, provider URLs, provider credentials, billing details, database connection strings, or personal data. If a public report needs sensitive evidence, first open a redacted issue asking for a private handoff path. For a Cloud availability check, inspect [`/api/health`](https://lasso.sh/api/health) and [`/agent.json`](https://lasso.sh/agent.json). Process health establishes only the checks reported by that response. It does not prove that a particular chain, provider, method, historical query, or subscription is working. The [public status page](https://lasso-rpc-status.checkly-status-page.com/) shows Cloud availability checks. It has a short operating history and does not establish that a particular chain, method, or subscription is working. There is no contractual incident-response target. During an incident, keep the previous RPC endpoint or self-hosted deployment available as an application rollback when your workload requires that option. ## Report a security vulnerability Do not use a public issue for a suspected vulnerability in Lasso Cloud or RPC Core. Email `jaxernst@gmail.com`. This is also the private reporting channel in the current [RPC Core security policy](https://github.com/jaxernst/lasso-rpc/security/policy). Include enough detail to reproduce and assess the report, but do not send active production credentials unless a separate secure exchange has been arranged. The current policy does not promise a response or disclosure deadline. Confirmed issues are investigated, fixed, and disclosed on a timeline coordinated with the reporter when practical. For operating guidance, see the Core [security considerations](https://github.com/jaxernst/lasso-rpc/blob/main/SECURITY.md#security-considerations-for-self-hosting) and [production checklist](/deployment/production-checklist). RPC Core has no built-in client authentication or incoming customer quotas. Protect its RPC, metrics, and dashboard endpoints at your network or reverse proxy. ## Versions and lifecycle ### RPC Core RPC Core supports the latest published release with security updates. Upgrade older versions before deploying them or requesting a security fix. The project is pre-1.0, so review the target release's compatibility section before every upgrade instead of assuming that all minor releases are interchangeable. Use a versioned GitHub release and its recorded container digest for production. Repository `main`, a moving container tag, and the separately deployed Cloud service can contain different code. Release notes state known configuration, storage, migration, and rollback boundaries. See [Versions, availability, and evidence](/releases-and-availability). There is no published long-term-support branch, backport window, or fixed release cadence today. Older releases remain available as historical artifacts, but their availability does not mean they receive fixes. ### Lasso Cloud Cloud is a continuously deployed managed service. Check the live [`/openapi.json`](https://lasso.sh/openapi.json) for request contracts and [`/agent.json`](https://lasso.sh/agent.json) for advertised capabilities before automation. A route present in source or documentation can still be disabled in the deployed service. There is no general public deprecation-notice window or fixed Cloud release cadence today. Preserve the request and response behavior your integration depends on, keep a rollback path, and recheck the live contracts during qualification and before expanding traffic. ## Repository and license boundaries | Surface | Purpose | Repository and license boundary | | - | - | - | | Lasso RPC Core | Self-hosted routing engine, dashboard, configuration, and release artifacts | Public [`jaxernst/lasso-rpc`](https://github.com/jaxernst/lasso-rpc), licensed under Apache-2.0. The repository license applies to the Core source and its distributed artifacts. | | Lasso Cloud | Managed accounts, keys, billing, entitlements, profiles, and hosted operations around the routing engine | The implementation is maintained in a private repository that currently declares AGPL-3.0. Availability of the managed service and access to that repository are separate from the Apache-2.0 Core distribution. Cloud does not necessarily run the latest Core release. | | Lasso documentation | Source for this documentation site | Public [`jaxernst/lasso-docs`](https://github.com/jaxernst/lasso-docs), licensed under MIT. That license covers the documentation source, not RPC Core or the managed service. | The live Cloud service, the latest Core release, and repository `main` are separate evidence surfaces. When evaluating behavior, record which one you tested, its reported source or version, the time, region, route, and workload.