Neutrino syncs headers and filters, not the chain
Privacy-Preserving Bitcoin Light Client
At a glance
- What is it?
- Neutrino is a Bitcoin light client written in Go, built with mobile Lightning Network clients in mind, and it uses compact block filters to keep bandwidth and storage down on the client side. The consequence is a specific pair of APIs, a rescan for wallets and a UTXO check for smart contract platforms, and a hard limit on zero confirmation transactions.
- Who is it for?
- Neutrino fits a team building a mobile or embedded Bitcoin wallet, or a Lightning node that has to run on a phone, because the whole design is about not downloading the chain, and the two query APIs it exposes, Rescan and GetUtxo, are exactly the two a wallet needs. It does not fit a use case that needs to see unconfirmed transactions, since the client does not support accepting zero confirmation transactions, and it does not fit a node that wants an archive of every block.
- 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 14 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Headers and filter headers, nothing else
Neutrino is a Bitcoin light client written in Go and designed with mobile Lightning Network clients in mind. The core idea is in the mechanism of operation. The light client synchronizes only block headers and a chain of compact block filter headers specifying the correct filters for each block. Filters are loaded lazily and stored in the database upon request, and blocks are loaded lazily and not saved. That last clause is the one to hold on to, because it means there is no block archive on the device. A full node keeps every block it has ever seen, and Neutrino keeps only the filters, which are small indexes describing what each block might contain, so a mobile client can verify what it cares about without holding gigabytes of chain data. The design intent is stated as minimizing bandwidth and storage use on the client side while attempting to preserve privacy and minimize processor load on full nodes serving light clients.
Compact filters are the privacy argument
The filters themselves are the interesting technical choice, and they come from a proposal rather than from the Bitcoin protocol itself. The README links a new proposal for compact block filters, which is what lets a client download a filter per block, check whether a transaction it cares about could be inside, and fetch the full block only when the answer might be yes. The privacy argument follows from the lazy loading. A client that only asks for the filters of blocks it needs, and only asks for a full block when the filter says the block might contain something relevant, reveals far less about its interests to the full nodes serving it than a client that downloads everything. The cost is a round trip per interesting block, and the CPU work of checking filters. The stated goal is a balance between the two, and it is why the project says it attempts to preserve privacy rather than guaranteeing it, since the filters are matched against a set of interests that a determined observer could still model.
Rescan is the wallet API
The query surface is what a consumer of the library actually touches, and there are two methods that matter. The first is Rescan, which allows a wallet to scan a chain for specific TXIDs, outputs and addresses. A start and end block may be specified along with other options, and the defaults are worth knowing. If no end block is specified, the rescan continues until stopped, and if no start block is specified, the rescan begins with the latest known block. While a rescan runs it notifies the client of each connected and disconnected block, and those notifications follow the btcjson format, with the option to use any of the relevant notifications. The limitation stated in the documentation is a real one for a mobile wallet. The recvtx and redeemingtx notifications are only sent when a transaction is confirmed, not when it enters the mempool, because the client does not currently support accepting zero confirmation transactions.
GetUtxo and the start block you must not forget
The second method is GetUtxo, which lets a wallet or a smart contract platform check that a UTXO exists on the blockchain and has not been spent. The scanning strategy is backwards. The client scans from the tip of the chain backwards, stopping when it finds the UTXO having been either spent or created, and if it finds neither it keeps scanning backwards until it hits the specified start block, or the first block in the blockchain if no start block is specified. The documentation is emphatic that specifying a start block is highly recommended, and the reason is a bandwidth trap: in the event that the UTXO does not exist on the blockchain, the client will download all the filters back to block one searching for it. The return value is a SpendReport that contains either a TxOut including the PkScript required to spend the output, or information about the spending transaction, the spending input and the block height in which the spending transaction was seen. That second case is what makes the call useful to a Lightning node, which needs to know not only that a UTXO is gone but who took it.
The package layout mirrors the sync stages
The top level of the repository is a Go package with a flat layout, and the directory names are the best description of the internals. There is chainsync/ for the header synchronisation, filterdb/ for the compact filter storage, headerfs/ for the header file store, query/ for the query layer, cache/ for the block and filter cache, and pushtx/ for transaction broadcasting, which is the one part of a light client that has to talk to full nodes in a different way. Notifications have their own directory in blockntfns/, and there is banman/ for peer banning. The main entry points are neutrino.go, blockmanager.go, rescan.go, query.go, utxoscanner.go, notifications.go and verification.go, with the test files sitting next to them, including a sync test and a rescan test. The dependency list tells you the ecosystem. It is built on btcd v0.26.0 and the btcd v2 modules for the wire protocol, scripts, addresses and chain configuration, on btcwallet for the transaction authoring and wallet database layers, and on a separate neutrino cache package, plus the lnd queue package for job scheduling, which is the one place the Lightning dependency reaches into the core.
Building and testing need btcd first
The Makefile is the entry point for development, and the first target anyone hits is the one that installs btcd. The build itself is a single compile of the package tree:
build:
@$(call print, "Compiling neutrino.")
$(GOBUILD) $(PKG)/...The test target depends on btcd, which tells you that the unit tests run against a real btcd instance rather than a mock, and the target itself lists the package dependencies and runs the tests over them:
unit: btcd
@$(call print, "Running unit tests.")
$(GOLIST) | $(XARGS) env $(GOTEST)There is a coverage variant that uses go-acc, a race detector variant, and a lint setup that uses golangci-lint, with a note that linting uses a lot of memory so the worker count can be limited with a workers argument. The tools are installed from a tools directory with the tools build tag, and there is a Docker based route for the toolchain through a neutrino-tools image. The Makefile also has a btcd target that installs the btcd binary into the GOPATH bin directory, which is the step that makes the unit target work at all.
The library, not a daemon
The usage section makes clear that Neutrino is a library rather than a service you run. The client is instantiated as an object using NewChainService and then started. Upon start, the client sets up its database and other relevant files and connects to the p2p network, and at that point it becomes possible to query the client. Stopping it means calling Stop on the ChainService, and the method does not return until the ChainService is cleanly shut down. The licence is MIT, the project is not archived, and the release history is short and recent, with v0.17.1 and v0.17.0 both published on 2026-05-21 and the last push on 2026-10-09. The version numbering being below one point zero is worth remembering for a dependency that other wallets and Lightning implementations build on, since a 0.x module makes no compatibility promise between minor versions, and the GitHub Actions build and Coveralls coverage badges are the visible sign that the project is still exercised in continuous integration.
Editorial conclusion
Neutrino fits a team building a mobile or embedded Bitcoin wallet, or a Lightning node that has to run on a phone, because the whole design is about not downloading the chain, and the two query APIs it exposes, Rescan and GetUtxo, are exactly the two a wallet needs. It does not fit a use case that needs to see unconfirmed transactions, since the client does not support accepting zero confirmation transactions, and it does not fit a node that wants an archive of every block. Before you build on it, read the GetUtxo documentation carefully and always pass a start block, since without one a UTXO that does not exist causes the client to download every filter back to block one, and check the version you pin, because the last tagged releases are v0.17.1 and v0.17.0 from 2026-05-21 while the last push was on 2026-10-09.
Frequently asked questions
What does Neutrino download from the Bitcoin network?
Only block headers and a chain of compact block filter headers. Filters are loaded lazily and stored in the database when requested, and blocks are loaded lazily and are not saved, so the client keeps no block archive.
How do I use Neutrino?
It is a Go library, so you instantiate the client with NewChainService and start it. It then sets up its database and files, connects to the peer to peer network, and can be queried until you call Stop on the ChainService.
Can Neutrino see unconfirmed transactions?
No. The client does not currently support accepting zero confirmation transactions, and the recvtx and redeemingtx notifications are only sent when a transaction is confirmed rather than when it enters the mempool.
Why does GetUtxo need a start block?
Because without one, a UTXO that does not exist on the blockchain makes the client download all the filters back to block one while searching backwards. The client scans from the tip backwards and stops when it finds the output spent or created.
Is Neutrino still maintained?
The repository is not archived and the last push was on 2026-10-09, with releases v0.17.1 and v0.17.0 published on 2026-05-21, and the licence is MIT.
Official sources
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.
[](https://hysenlabs.com/projects/lightninglabs-neutrino)Was this useful?