documentation

architecture

zcli

the wallet. derives orchard spending keys and transparent keys from an ssh ed25519 seed via BLAKE2b-512("ZcliWalletSeed" || ed25519_seed). trial-decrypts compact blocks locally — the server never learns which addresses belong to you. builds and proves orchard transactions with halo2 locally.

zidecar

the backend. indexes the zcash chain into nomt and generates ligerito proofs over the header chain (gigaproof + tip proof). serves compact blocks and proofs over gRPC-web. default endpoint: zcash.rotko.net.

zync-core

shared verification library. ligerito proof verification, nomt merkle proof verification, actions commitment chain, trial decryption. used by both zcli and zafu.

client-side verification

zcli verifies chain data before trusting it. nothing from the server is accepted without cryptographic proof. run zcli verify to see the full chain:

$ zcli verify
zcli verify - trustless verification chain
network:  mainnet
endpoint: https://zcash.rotko.net
tip:      3266078 (000000000075b2db)

1. trust anchor
   hardcoded orchard activation hash at height 1687104
   server returned: 0000000000d72315
   expected:        0000000000d72315
   PASS

2. cross-verification (1 independent node)
   https://zec.rocks - tip matches
   consensus: 1/1 agree (threshold: >2/3)
   PASS

3. header chain proof (ligerito)
   gigaproof: 1687104 -> 3265535 (1578432 headers, 452 KB)
   gigaproof anchored to activation hash: PASS
   gigaproof cryptographic verification:  PASS
   tip proof: 3265536 -> 3266078 (543 headers)
   tip proof cryptographic verification:  PASS
   chain continuity (tip chains to giga): PASS
   total blocks proven: 1578974

4. cryptographically proven state roots
   (extracted from ligerito polynomial trace sentinel row)
   tree_root:          b375422028a896ed...
   nullifier_root:     512ab0f1f95c751e...
   actions_commitment: 18001392bc7a253b...
   proof freshness: 0 blocks behind tip

all checks passed

trust anchor

the orchard activation block hash (height 1,687,104) is hardcoded in the binary. every proof chain must start from this immutable anchor. the gigaproof's start_hash is checked against it.

cross-verification

tip block hash is compared against independent lightwalletd nodes (default: zec.rocks, configurable via --verify-endpoints). requires BFT majority (>2/3 agreement). hard-fails on mismatch or when no nodes respond.

header chain proof (ligerito)

the gigaproof (~300KB) covers the header chain from NU5 activation to the latest epoch boundary. the tip proof (~150KB) covers from there to the current tip, regenerated every block. both are verified cryptographically and must chain continuously.

the ligerito proof commits to a polynomial trace containing a sentinel row with three state roots. these are cryptographically bound to the proven header chain — the server cannot forge them.

proven state roots

the sentinel row in the ligerito trace contains:

tree_root          orchard commitment tree root (nomt)
nullifier_root     nullifier set root (nomt)
actions_commitment running chain of per-block action merkle trees

what happens during sync

during zcli sync, each received note gets a nomt merkle proof verified against the proven tree_root. each unspent nullifier gets a nomt proof against the proven nullifier_root. the client recomputes per-block action merkle trees and chains them — the result must match the proven actions_commitment. after trial decryption, cmx is recomputed from the decrypted note and compared to the server-provided value. notes are only stored after all proofs pass.

all verification hard-fails. no soft-fails, no warnings-instead-of-errors.

remaining trust assumption

at least one of your cross-verification endpoints must be honest about the chain tip. if all endpoints collude and serve a forked chain, no light client can detect this. this is fundamental to all light clients.

usage reference

zcli -i ~/.ssh/id_ed25519 address          # show addresses
zcli sync                                   # scan chain
zcli balance                                # check funds
zcli send 0.01 u1...                        # send shielded
zcli shield                                 # t-addr → shielded
zcli receive                                # QR code
zcli notes --json                           # list received notes
zcli export                                 # export viewing key
zcli board --port 3333                      # sync loop + JSON API

all commands accept --json for machine-readable output. set ZCLI_IDENTITY, ZCLI_ENDPOINT as env vars for headless operation.

air-gapped signing (with zigner)

zcli import-fvk --cam                      # scan FVK QR from zigner
zcli sync --wallet zigner                  # sync watch-only wallet
zcli balance --wallet zigner               # check balance
zcli send 1.0 u1... --wallet zigner --cam # build unsigned tx, scan signed QR

see zigner.rotko.net for the hardware signer.

environment variables

ZCLI_IDENTITY    path to ed25519 ssh key (default: ~/.ssh/id_ed25519)
ZCLI_ENDPOINT    gRPC-web endpoint (default: zcash.rotko.net)
SSH_PASSPHRASE   key passphrase for non-interactive use