# 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.