Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Zaino
Zaino is an indexer for the Zcash blockchain implemented in Rust.
Zaino provides all necessary functionality for "light" clients (wallets and other applications that don't rely on the complete history of blockchain) and "full" clients / wallets and block explorers providing access to both the finalized chain and the non-finalized best chain and mempool held by a Zebra full validator.
Release pipeline
Zaino ships through a gated pipeline (dev → rc → release-ready → stable); the
gates, tags, and blessing flow are specified in the
release decision record.
| Gate | Workflow |
|---|---|
Changeset check (dev-gate) |
|
| RC gate (nightly) | |
| Blessing (release) |
The "latest RC" badge shows the newest cycle-<N>-rc.<M> prerelease tag; "latest
release" filters those out to show the newest blessed cycle-<N> tag. (Both use
shields.io tag filtering; if a shields update ever stops distinguishing the two,
fall back to a single unfiltered github/v/tag "latest cycle tag" badge.)
Motivations
With the ongoing legacy-full-node deprecation project, there is a push to transition to a modern, Rust-based software stack for the Zcash ecosystem. By implementing Zaino in Rust, we aim to modernize the codebase, enhance performance and improve overall security. This work will build on the foundations laid down by Librustzcash and Zebra, helping to ensure that the Zcash infrastructure remains robust and maintainable for the future.
Due to current potential data leaks / security weaknesses highlighted in revised-nym-for-zcash-network-level-privacy and wallet-threat-model, there is a need to use anonymous transport protocols (such as Nym or Tor) to obfuscate clients' identities from Zcash's indexing servers (Lightwalletd, the legacy Zcash full node, Zaino). As Nym has chosen Rust as their primary SDK (Nym-SDK), and Tor is currently implementing Rust support (Arti), Rust is a straightforward and well-suited choice for this software.
Zebra has been designed to allow direct read access to the finalized state and RPC access to the non-finalized state through its ReadStateService. Integrating directly with this service enables efficient access to chain data and allows new indices to be offered with minimal development.
Separation of validation and indexing functionality serves several purposes. First, by removing indexing functionality from the Validator (Zebra) will lead to a smaller and more maintainable codebase. Second, by moving all indexing functionality away from Zebra into Zaino will unify this paradigm and simplify Zcash's security model. Separating these concerns (consensus node and blockchain indexing) serves to create a clear trust boundary between the Indexer and Validator allowing the Indexer to take on this responsibility. Historically, this had been the case for "light" clients/wallets using Lightwalletd as opposed to "full-node" client/wallets and block explorers that were directly served by the the legacy Zcash full node.
Goals
Our primary goal with Zaino is to serve all non-miner clients -such as wallets and block explorers- in a manner that prioritizes security and privacy while also ensuring the time efficiency critical to a stable currency. We are committed to ensuring that these clients can access all necessary blockchain data and services without exposing sensitive information or being vulnerable to attacks. By implementing robust security measures and privacy protections, Zaino will enable users to interact with the Zcash network confidently and securely.
To facilitate a smooth transition for existing users and developers, Zaino is designed (where possible) to maintain backward compatibility with Lightwalletd and the legacy Zcash full node. This means that applications and services currently relying on these platforms can switch to Zaino with minimal adjustments. By providing compatible APIs and interfaces, we aim to reduce friction in adoption and ensure that the broader Zcash ecosystem can benefit from Zaino's enhancements without significant rewrites or learning curves.
Scope
Zaino will implement a comprehensive RPC API to serve all non-miner client requests effectively. This API will encompass all functionality currently in the LightWallet gRPC service (CompactTxStreamer), currently served by Lightwalletd, and a subset of the Zcash RPCs required by wallets and block explorers, currently served by the legacy Zcash full node. Zaino will unify these two RPC services and provide a single, straightforward interface for Zcash clients and service providers to access the data and services they require.
In addition to the RPC API, Zaino will offer a client library allowing developers to integrate Zaino's functionality directly into their Rust applications. Along with the RemoteReadStateService mentioned below, this will allow both local and remote access to the data and services provided by Zaino without the overhead of using an RPC protocol, and also allows Zebra to stay insulated from directly interfacing with client software.
Currently Zebra's ReadStateService only enables direct access to chain data (both Zebra and any process interfacing with the ReadStateService must be running on the same hardware). Zaino will extend this functionality, using a Hyper wrapper, to allow Zebra and Zaino (or software built using Zaino's IndexerStateService as its backend) to run on different hardware and should enable a much greater range of deployment strategies (eg. running validator, indexer or wallet processes on separate hardware). It should be noted that this will primarily be designed as a remote link between Zebra and Zaino and it is not intended for developers to directly interface with this service, but instead to use functionality exposed by the client library in Zaino (IndexerStateService).
Project Structure
packages/ Cargo workspace member crates, in dependency order
zaino-status/ How a component reports whether it is working
zaino-component/ Supervised subsystems: lifecycle, health, and tasks
zaino-consensus/ Zcash consensus constants and protocol limits
zaino-encoding/ Versioned on-disk encoding traits and byte helpers
zaino-primitives/ Domain vocabulary (thiserror only; no serde)
zaino-address/ Zcash address classification
zaino-source/ Driven ports: one trait per chain question
zaino-rpc/ JSON-RPC transport (no parsing)
zaino-convert-zebra/ zebra-chain -> domain conversions
zaino-source-zebra-rpc/ JSON-RPC adapter + response parsing
zaino-source-zebra-readstate/ Zebra ReadStateService adapter
zaino-source-zebra/ ZebraValidator composite + routing
zaino-mempool/ Mempool domain types and ports (no node library)
zaino-mempool-service/ The mempool runtime: poll loop, read handles, coherence
zaino-common/ Shared utilities and configuration
zaino-proto/ Protocol buffer definitions
zaino-chain-head/ Non-finalised chain head: vocabulary and ports
zaino-chain-head-service/ Non-finalised chain head: the runtime
zaino-chain-store/ Finalised state: vocabulary and ports
zaino-chain-store-zainodb/ Finalised state: the LMDB implementation
zaino-state/ Chain state and indexer service library
zaino-serve/ gRPC + JSON-RPC servers, and the served JSON schema
zainod/ Daemon binary
live-tests/ Live-test suite — standalone workspace, run on the ztest k8s harness
e2e/ End-to-end partition (wallet client -> Zaino -> validator)
clientless/ Clientless partition (Zaino services -> live validator, no client)
zaino-testutils/ Shared test harness and utilities
docs/ Architecture diagrams, specs, and usage guides
tools/ Development tools
workbench/ Repo guards run by `makers lint`
relman/ Release manager
.github/ CI workflows and issue templates
.githooks/ Git hooks (pre-push)
Cargo.toml Top-level workspace manifest
Cargo.lock Resolved dependency graph (committed)
Makefile.toml cargo-make task definitions
rust-toolchain.toml Pinned Rust toolchain
deny.toml cargo-deny policy (licenses, advisories)
Dockerfile Container image
entrypoint.sh Container entrypoint
.dockerignore Docker build context exclusions
README.md This file
CHANGELOG.md Release notes
CLAUDE.md AI-contributor guidelines
CONTRIBUTING.md Human-contributor guide
LICENSE Apache-2.0 license text
.gitignore Git ignore patterns
Server network exposure
Zaino exposes two servers and one optional telemetry listener, with different defaults reflecting their transport security:
-
gRPC (
[grpc_settings]): may bind to a public address only when TLS is configured ([grpc_settings.tls]withcert_path/key_path). Binding to a non-private address without TLS is rejected at startup. Theno_tls_use_unencrypted_trafficbuild feature disables this enforcement (and logs a startup warning) — for testing or trusted networks only. -
JSON-RPC (
[json_server_settings]): has no transport encryption and is intended for loopback or trusted private networks only. By default it may bind only to private/loopback addresses (RFC1918, IPv6 ULA, or loopback); public or unspecified (0.0.0.0/::) bind addresses are rejected at startup. Theallow_unencrypted_public_json_rpc_bindbuild feature lifts this restriction (and logs a startup warning) for deployments on trusted private networks where encryption is handled externally (e.g. containers behind a service mesh or proxy that terminates TLS). -
Admin listener (
metrics_endpoint, featureprometheus): off unless set./metrics,/livez(/readyz: TODO) on its own thread + runtime — seezainod's guide. Unauthenticated + unencrypted, publishes chain tip, sync progress, request volumes, process memory. Non-private bind warned, not rejected → restrict to loopback, a private interface, or the scraper's network.
Security implication: the JSON-RPC interface transmits unencrypted traffic.
Do not expose it to untrusted networks, and only enable
allow_unencrypted_public_json_rpc_bind when an external layer secures the
connection.
Container image
Dockerfile: rust:<pin> build → debian-slim runtime, non-root container_user.
| Build arg | Values | Default |
|---|---|---|
CARGO_FEATURES |
comma-separated, e.g. prometheus, no_tls_use_unencrypted_traffic |
empty (default set) |
CARGO_PROFILE |
release, profiling (+ line tables & frame pointers, for sampling profilers) |
release |
RUSTFLAGS="-C force-frame-pointers=yes"
Running tests
Production-crate tests run on your host; the live suites run on a Kubernetes
cluster via ztest:
&&
The live suites need the ztest CLI and a registered cluster
(cargo install ztest_cli, then kind create cluster and ztest cluster setup) — see docs/testing.md for the full setup.
On lower-resource machines you may hit occasional contention flakes under full
parallelism — re-run, or lower --test-threads.
Documentation
- Use Cases: Holds instructions and example use cases.
- Testing: Holds instructions for running tests.
- Live-test guidelines: The rules for writing live tests — the live oracle, QoS tiers, parameterization.
- Live Service System Architecture: Holds the Zcash system architecture diagram for the Zaino live service.
- Library System Architecture: Holds the Zcash system architecture diagram for the Zaino client library.
- ZainoD (Live Service) Internal Architecture: Holds an internal Zaino system architecture diagram.
- Zaino-State (Library) Internal Architecture: Holds an internal Zaino system architecture diagram.
- Internal Specification: Holds a specification for Zaino and its crates, detailing their functionality, interfaces and dependencies.
- RPC API Spec: Holds a full specification of all of the RPC services served by Zaino.
- Cargo Docs: Holds a full code specification for Zaino.
Architecture Decision Records
Decisions that shape the codebase, with the reasoning that produced them. Read
these before changing the structure they describe. Every record lives in
zingolabs/zingo-adrs. This
repository checks in only a submodule pointer to it at docs/adr/, so the
directory is empty until you materialise it, and zaino's own records then sit
under docs/adr/zaino/. Propose a record in zingo-adrs, never here.
# materialise the records after cloning
# advance the pointer to the current dev of zingo-adrs, then commit
Records a newcomer needs first:
- ADR-0006: aws-lc-rs as the preferred rustls CryptoProvider.
- ADR-0007: block persistence is a row-set boundary.
- ADR-0008: validator access is a set of single-question ports over domain primitives.
- ADR-0009: the served JSON schema lives in
zaino-serve. - ADR-0010: the mempool subsystem is separated into
zaino-mempoolbehind ports. - ADR-0011: the non-finalised chain head is a self-synchronising subsystem.
- ADR-0012: the finalised state is a subsystem behind ports, and its database is one implementation of them.
Crate usage guides
Practical guidance for working in a crate — its scope, its invariants, and the mistakes its design is trying to prevent.
zaino-status: the status vocabulary, and why it stays vocabulary.zaino-component: the component abstraction, its two independent axes, and the observed/owned line.zaino-consensus: the protocol constants, and why they are stated rather than borrowed.zaino-primitives: the domain vocabulary, and why it depends on nothing.zaino-persistence: the storage backend port, and why index code never names a concrete store.zaino-source: the ports, the domain/fetch error split, andResilient.zaino-rpc: JSON-RPC transport, and what it does not do.zaino-convert-zebra:zebra-chain→ domain conversions.zaino-source-zebra-rpc: the JSON-RPC adapter and its error classification.zaino-source-zebra-readstate: the read-state adapter, and what it cannot answer.zaino-source-zebra: the composite and its three routing rules.zaino-address: address classification, and what is not classified.zaino-mempool: the two-layer model, the ports, and the bounds.zaino-mempool-service: spawning and consuming the mempool.zaino-chain-head: the chain head's ports, why reads live on the snapshot, and why there is no way to make it synchronise.zaino-chain-head-service: the chain head runtime, its two testing styles, and the properties to keep when editing the advance path.zaino-encoding: the versioned record format, and why nested fields must have their version pinned.zaino-chain-store: the finalised state's ports, why the chunk is the block-read primitive, and why a read past the watermark is not a miss.zaino-chain-store-zainodb: the LMDB store, its on-disk compatibility contract, and why its checksums are load-bearing.zaino-serve: served RPCs.zainod: the admin listener — what goes on/metrics,/metrics(quantities) vs/readyz(per-component health & modes).
Security Vulnerability Disclosure
If you believe you have discovered a security issue, and it is time sensitive, please contact us online on Matrix. See our CONTRIBUTING.md document for contact points. Otherwise you can send an email to: zingodisclosure@proton.me
License
This project is licensed under the Apache License 2.0. See the LICENSE file for details.