Open-source project
a16z/helios avatar
a16z/helios

Helios verifies your RPC provider against a 32-byte checkpoint

A fast, secure, and portable multichain light client for Ethereum

2,187 stars462 forksRustMIT

At a glance

What is it?
Helios is a Rust light client that takes an untrusted execution layer RPC endpoint, checks what it returns against the consensus layer, and serves the result as a local JSON-RPC server, so a wallet or dapp can verify its own data without running a node. The trust model is one weak subjectivity checkpoint, and the README is unusually candid about which flags weaken it.
Who is it for?
Adopt Helios if you are building a wallet or dapp that must not take an RPC provider's word for a balance or a code hash, and if you can ship or vendor a WebAssembly build, since that is where its footprint stops mattering. Do not adopt it if you need archival state, tracing or a full node's guarantees, because a light client verifies execution against consensus and does not hold the chain.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An untrusted RPC in, a verified local RPC out

The pitch is one sentence in the README: Helios converts an untrusted centralized RPC endpoint into a safe unmanipulable local RPC for its users. Concretely, you hand it an execution layer URL, it cross-checks execution data against the consensus layer using the light client beaconchain API, and it serves a local JSON-RPC server on 127.0.0.1:8545. The cost model is the other half of the pitch: it syncs in seconds, requires no storage, and is light enough to run on a phone, with a small binary that also compiles to WebAssembly so it can be embedded directly in a wallet or a dapp instead of shipped as a daemon. The storage claim is literal rather than rhetorical. The data directory exists only to hold cached weak subjectivity checkpoints, one 32-byte value per network. Everything else is recomputed. That is the difference between Helios and a node, and it is why the interesting questions about the project are all about trust rather than about performance.

One checkpoint is the root of trust, so it matters which one

A light client cannot verify the earliest history, so it needs a place to start from, and that place is the weak subjectivity checkpoint. The README is direct about it: the checkpoint must equal the first beacon block hash of an epoch, and weak subjectivity checkpoints are the root of trust in the system. If the value is malicious, an attacker can cause the client to sync to the wrong chain, which is a statement about the entire security of the setup in one sentence. Two details matter in practice. The checkpoint is a consensus layer block hash, not an execution layer block hash, and the README shows an Etherscan block page as a counter-example of the wrong thing to copy. And Helios does not ask you to supply one every time: it sets a default initially, then caches the most recent finalized block it has seen for later use, which is what makes the 32-byte data directory worth having. Checkpoints for mainnet and Holesky can be obtained from beaconcha.in and holesky.beaconcha.in, and the README recommends using a block hash as the check rather than a height or a date.

Checkpoint age is a policy flag, and the default is the loose one

There is one flag in this tool that deserves a decision rather than a default, and it is --strict-checkpoint-age or -s. The behaviour without it is gentle: if the checkpoint is more than two weeks old, Helios surfaces a warning and continues. With the flag, it errors instead. The reason for the default is given plainly in the README, that there are theoretical attacks which can cause Helios and other light clients to sync incorrectly, and that these attacks are complex and expensive, so it is disabled by default. That is a reasonable engineering judgement and it is also a real gap in protection, because the entire trust model rests on that one value and its freshness. If you are running Helios in production for other people, enable the flag and accept that you now own the job of keeping the checkpoint current, which means watching for the case where the client can no longer sync and you need a fresh one. The other end of the same problem is handled by --fallback, discussed next, and by the age of the value in the first place.

The external fallback exists, and the README says not to trust it

When a checkpoint is too old to sync from, Helios needs somewhere else to look, and it offers two ways. --fallback or -f takes a single URL, for example a beacon synchronisation endpoint, and uses it if the checkpoint you passed is too outdated. --load-external-fallback or -l takes no value and goes further: it queries every API in a community-maintained list at ethpandaops/checkpoint-sync-health-checks, filters for healthy endpoints, and returns the most frequent checkpoint occurring in the latest epoch. The README's own framing is unambiguous, calling it a last resort if the checkpoint passed into --checkpoint fails, noting that the list is community-maintained so no security guarantees are provided, and warning that this is not recommended because malicious checkpoints can be returned from those APIs even when they are considered healthy. A tool that documents its least trustworthy path in its own README is doing you a favour. Set a specific fallback you control, keep -l for the case where you are debugging, and do not put it in a production startup path.

Three subcommands, and one provider requirement

Chains are selected by subcommand rather than by configuration alone. For Ethereum:

bash
helios ethereum --execution-rpc $ETH_RPC_URL

For an OP Stack chain, which adds a network argument, with op-mainnet and base named as the currently supported values:

bash
helios opstack --network $NETWORK --execution-rpc $ETH_RPC_URL

And for Linea, which takes only the execution RPC. The hard constraint in all three cases is the provider: the URL must be a supported Ethereum Execution API provider that offers the eth_getProof endpoint, and the README recommends Alchemy or Infura. That single requirement decides which vendors you can use, and it is worth checking before anything else, since a provider without eth_getProof cannot be verified and Helios will not work with it. The remaining networking options are ordinary: --consensus-rpc or -c points at a consensus node supporting the light client beaconchain API, defaulting to a service the maintainers run, with Nimbus recommended; --network or -n accepts mainnet, sepolia and holesky, with custom networks added in the config file. The set of RPC methods Helios actually answers is written down separately in rpc.md, which is the file to read if you are writing a client against it.

The install is a curl piped into a shell

Installation is two steps, and the first is this:

bash
curl https://raw.githubusercontent.com/a16z/helios/master/heliosup/install | bash

That fetches the heliosup installer from the master branch of the GitHub repository and executes it, and then you run heliosup. As a matter of practice, read the install script before you pipe it anywhere: the URL points at a branch rather than at a tag, so the content is whatever master holds at the moment you run it, and there is no checksum in the documented flow. That is a normal trade-off for a small binary and an unreasonable one for a component holding a checkpoint that guards chain identity, so the sensible sequence is to fetch the script, read it, and run it. The rest of the surface is a Rust workspace you can build with cargo, with the default member being the cli, so a from-source build is a normal cargo build rather than a special procedure.

Per-network config, and API keys end up on disk

Everything on the command line can also live in a file at ~/.helios/helios.toml, keyed by network, and the README shows the shape with three sections:

toml
[mainnet]
consensus_rpc = "https://ethereum.operationsolarstorm.org"
execution_rpc = "https://eth-mainnet.g.alchemy.com/v2/XXXXX"
checkpoint = "0x85e6151a246e8fdba36db27a0c7678a575346272fe978c9281e13a8b26cdfa68"

[op-mainnet]
consensus_rpc = "https://op-mainnet.operationsolarstorm.org"
execution_rpc = "https://opt-mainnet.g.alchemy.com/v2/XXXXX"

[base]
consensus_rpc = "https://base.operationsolarstorm.org"
execution_rpc = "https://base-mainnet.g.alchemy.com/v2/XXXXX"

The checkpoint appears only under mainnet in the example, which tells you something: the other networks have no pinned trust anchor in that file and rely on the default. Note also what the file holds, an execution RPC URL with an API key embedded in it, in plain text, in a user directory. That is normal for this kind of configuration and worth a thought before you commit one. The full option list is in config.md, and the repository also keeps a .env.example with MAINNET and SEPOLIA consensus and execution endpoints, which is the shape used for tests.

A library, a TypeScript binding and a verifiable API

The repository is a Cargo workspace with more in it than the CLI. Members include cli, common, core, ethereum with a consensus-core subcrate, opstack, linea, revm-utils, helios-ts, and a verifiable-api split into client, server and types, plus a test utilities crate. Several of those names tell you where the project is going. helios-ts is the WebAssembly surface, the one that lets a wallet verify without a daemon. verifiable-api is a server and a client pair, which suggests serving verified data to clients that cannot run the verification themselves. revm-utils, alongside a revm 29.0.1 dependency with an explicitly trimmed feature set, points at EVM execution rather than only header and state verification. The rest is a normal Ethereum stack: alloy 1.0.37 for RPC types and signers, SSZ and tree hash crates for consensus encoding, bls12_381, tokio, and reqwest with hickory-dns. There are five worked examples in the examples directory, basic.rs, call.rs, checkpoints.rs, client.rs and config.rs, and checkpoints.rs is the one to read first if you are embedding it.

Editorial conclusion

Adopt Helios if you are building a wallet or dapp that must not take an RPC provider's word for a balance or a code hash, and if you can ship or vendor a WebAssembly build, since that is where its footprint stops mattering. Do not adopt it if you need archival state, tracing or a full node's guarantees, because a light client verifies execution against consensus and does not hold the chain. Verify five things before you rely on it: that your execution provider actually serves eth_getProof, since that endpoint is the hard requirement, that your checkpoint is a beacon chain block hash at the first block of an epoch rather than an execution block hash, that you decide deliberately about --strict-checkpoint-age, that you read the heliosup install script rather than piping it straight to a shell, and whether master has moved past the 0.11.1 tag from 2026-02-27, the last push being 2026-09-25.

Frequently asked questions

What does Helios do that a normal RPC provider does not?

It verifies the execution data it receives from your RPC provider against the consensus layer, using the light client beaconchain API and an eth_getProof capable endpoint, then serves the result as a local RPC on 127.0.0.1:8545. The provider is treated as untrusted input.

Which chains does Helios support?

Ethereum, the OP Stack chains and Linea. The subcommands are helios ethereum, helios opstack and helios linea, and the README names op-mainnet and base as the currently supported OP Stack networks, with mainnet, sepolia and holesky as network values.

What is a weak subjectivity checkpoint in Helios?

It is the trust root: a beacon chain block hash equal to the first beacon block hash of an epoch. The README notes that a malicious checkpoint could cause the client to sync to the wrong chain, and that Helios sets a default and then caches the most recent finalized block it has seen.

Should I enable --strict-checkpoint-age?

It depends on who else is relying on the client. Without it, a checkpoint older than two weeks produces a warning and Helios continues; with it, the client errors. The README leaves it off by default because the attacks it guards against are described as complex and expensive.

Is it safe to use --load-external-fallback?

The README calls it a last resort and not recommended. It queries a community-maintained endpoint list, filters for healthy APIs and returns the most frequent checkpoint in the latest epoch, and the README states no security guarantees are provided and that malicious checkpoints can still be returned.

How do I install and run Helios?

The documented path is to fetch the heliosup installer with curl piped to bash and then run heliosup, after which helios ethereum --execution-rpc with a provider URL such as Alchemy starts a local RPC on 127.0.0.1:8545. The installer URL points at the master branch, so reading the script before running it is worth the extra minute.

Official sources

  1. a16z/helios on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/a16z-helios.svg)](https://hysenlabs.com/projects/a16z-helios)