Skip to content

ci: ship both zebra-state and zaino backends in one image + deb#538

Merged
y4ssi merged 3 commits into
mainfrom
ci/dual-backend-zaino
Jul 2, 2026
Merged

ci: ship both zebra-state and zaino backends in one image + deb#538
y4ssi merged 3 commits into
mainfrom
ci/dual-backend-zaino

Conversation

@y4ssi

@y4ssi y4ssi commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Problem

Zallet's chain-data backend is a compile-time, mutually-exclusive Cargo feature:

  • zebra-state (default) — reads a co-located zebrad's state directly via ReadStateService; requires a zebrad built with the non-default indexer feature + a shared state dir; no regtest.
  • zaino (--no-default-features --features zaino) — talks to Zebra over JSON-RPC in separate containers; supports regtest.

The published v0.1.0-alpha.4 image ships only the default zebra-state backend, so it can't run in split-container stacks like z3: zallet start fails with the zebra-state backend requires an [indexer.read_state_service] config section. Because the backend is fixed at Zallet compile time, bumping Zebra/Zaino doesn't help — a zaino-backend image has to be published. This blocks z3#49.

What this does

Build both backends and ship them together, additively — the existing zallet command is unchanged. Three invocable names:

Name Kind Backend Needs zebrad built with indexer? Regtest
zallet real binary zebra-state (default) Yes No
zallet-zebra-state symlink → zallet zebra-state Yes No
zallet-zaino real binary zaino No Yes

zallet is the default and is byte-for-byte the previous default build. zallet-zebra-state is just an explicit alias (a symlink to the same binary). zallet-zaino is the only new binary.

Which one should I run? zallet / zallet-zebra-state (zebra-state) only work against a zebrad built with the non-default indexer feature (plus a shared state dir). If your zebrad is not built with indexer — including the stock zfnd/zebra images, the z3 stack, any setup where Zebra and Zallet are separate containers over JSON-RPC, or regtest — run zallet-zaino. All three share the same CLI surface, config format, and subcommands; only the chain-data backend differs.

Resulting artifacts

Deliverable zebra-state (default) zaino
Docker image zodlinc/zallet:<tag> /usr/local/bin/zallet + zallet-zebra-state (symlink); ENTRYPOINT = zallet /usr/local/bin/zallet-zaino
Debian package zallet (single package) /usr/bin/zallet + /usr/bin/zallet-zebra-state (symlink) /usr/bin/zallet-zaino
Standalone binary (GitHub Release) zallet-<version>-linux-<arch> zallet-<version>-linux-<arch>-zaino

One image and one .deb carry both backends; you pick by which binary you run. (Standalone binaries stay single-binary by nature, so the zaino one is a separate -zaino file. zallet-zebra-state is a symlink only in the image + deb, where a filesystem exists.)

Changes

  • Dockerfile.stagex — two cargo install passes (--no-default-features --features {zebra-state,zaino},rpc-cli,zcashd-import); zaino installs to a separate --root and is renamed zallet-zaino; a zallet-zebra-state -> zallet symlink is created. Runtime ships all three names; ENTRYPOINT stays zallet.
  • flake.nix (arm64/Nix) — parametrized mkZallet; exposes packages.zallet (default) + packages.zallet-zaino. Only the default carries the completions/manpages share tree (identical CLI surface).
  • build-arm64-nix.yml — builds both flake packages; stages both binaries + the zallet-zebra-state symlink into the shared runtime artifact.
  • binaries-and-deb-release.yml — seeds both binaries + the symlink for cargo-deb; signs + SBOMs both standalone binaries; one attestation covers both.
  • zallet/Cargo.toml — cargo-deb assets add zallet-zaino and the zallet-zebra-state symlink (cargo-deb ^2.10 preserves symlink assets).
  • docs — installation guide explains the three names, the indexer requirement, and when to pick zallet-zaino.

Notes

  • The default (zallet / zebra-state) is byte-for-byte the prior default: --no-default-features --features zebra-state,... is just the explicit form of the previous implicit default = ["zebra-state"].
  • The full multi-arch (StageX amd64 + Nix arm64) + SLSA/cosign/SBOM pipeline is preserved; both binaries flow through the existing single-image/single-deb release, so release.yml needs no change.
  • CI already tests both backends separately (ci.yml), so the feature strings are proven.

After merge

Dispatch a re-release with full SLSA (no re-tag): gh workflow run release.yml -f tag=v0.1.0-alpha.4. Then z3 re-points Z3_ZALLET_IMAGEzodlinc/zallet:v0.1.0-alpha.4 and runs zallet-zaino, unblocking z3#49.

Docs: supersedes #534

#534 (@pacu, "document zaino backend selection") was closed unmerged in favor of this PR. Since both touched the same doc files (book/src/guide/installation/README.md + setup.md), its content is carried here: the "Choosing a chain backend" section with the comparison table (including the Linux-only vs cross-platform distinction), the cargo install --no-default-features --features zaino build commands, and the mutual-exclusivity compile-error warning. This PR extends that with the pre-built-artifact story — the zallet / zallet-zebra-state / zallet-zaino binaries shipped in the Docker image + Debian package. Also closes #533.

y4ssi added 2 commits July 1, 2026 17:59
Zallet's chain-data backend is a compile-time, mutually-exclusive Cargo
feature: `zebra-state` (default; reads a co-located zebrad's state directly,
needs the `indexer` feature + shared state dir, no regtest) or `zaino` (talks
to Zebra over JSON-RPC in separate containers, supports regtest). The published
alpha.4 image only had the default zebra-state backend, so it can't run in the
z3 stack (`zallet start` -> "the zebra-state backend requires an
[indexer.read_state_service] config section").

Build BOTH binaries and ship them together, additively (the existing `zallet`
command is unchanged):

- `zallet`        -> zebra-state (default, unchanged name/entrypoint/docs)
- `zallet-zaino`  -> zaino backend (new, additive)

One Docker image and one Debian package now carry both binaries; consumers pick
the backend by which binary they run (z3 sets its command to `zallet-zaino`).
Standalone GitHub Release binaries gain a `-zaino` variant.

- Dockerfile.stagex: two `cargo install` passes (--no-default-features
  --features {zebra-state,zaino},rpc-cli,zcashd-import); the zaino build is
  installed to a separate --root and renamed to zallet-zaino. Runtime ships
  both; ENTRYPOINT stays `zallet`.
- flake.nix (arm64/Nix): parametrized `mkZallet`; exposes packages.zallet
  (default) + packages.zallet-zaino. Only the default carries the
  completions/manpages share tree (same CLI surface).
- build-arm64-nix.yml: builds both flake packages, stages both binaries into
  the shared runtime artifact.
- binaries-and-deb-release.yml: seeds both binaries for cargo-deb; signs/SBOMs
  both standalone binaries; one attestation covers both.
- zallet/Cargo.toml: cargo-deb assets add target/release/zallet-zaino.
- docs: installation guide explains the two backends / binaries.

The default (`zallet` / zebra-state) is byte-for-byte the prior default;
--no-default-features --features zebra-state,... is just the explicit form of
the previous implicit default. Only the additive zaino binary is new.
Ship a third invocable name so the default (zebra-state) backend is nameable
both ways: `zallet` (unchanged) and `zallet-zebra-state` (symlink -> zallet,
same binary). `zallet-zaino` stays the real second binary. Applied in the
Docker runtime, the .deb (cargo-deb preserves the symlink asset), and the
arm64 Nix staging. Docs updated to note zallet/zallet-zebra-state need a zebrad
built with the `indexer` feature, and that users without it should run
zallet-zaino.
…docs

Incorporate pacu's PR #534 ("Choosing a chain backend") so both doc efforts
tell one coherent story on the same files, and this PR supersedes #534:

- installation/README.md: adopt the "Choosing a chain backend" heading +
  #534's comparison table (adds the Linux-only vs cross-platform distinction),
  the source-build `cargo install --no-default-features --features zaino`
  commands, and the mutual-exclusivity compile-error warning. Extend it with
  the pre-compiled-artifact split (zallet / zallet-zebra-state / zallet-zaino
  in the image + deb; the -zaino standalone binary).
- setup.md: add #534's cross-reference from the chain-state section to the new
  installation content.

Closes #533 (same issue #534 targeted).

@nuttycom nuttycom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK; please expedite, Binance is requesting this.

@y4ssi
y4ssi merged commit 4fa38e1 into main Jul 2, 2026
28 checks passed
@nuttycom
nuttycom deleted the ci/dual-backend-zaino branch July 6, 2026 22:24
y4ssi pushed a commit to y4ssi/wallet-ci-test that referenced this pull request Jul 8, 2026
PR zcash#538 added the additive zallet-zaino binary to both runtime export
paths (Dockerfile.stagex export stage + build-arm64-nix.yml), but the
"Stage exported binaries" step in binaries-and-deb-release.yml only
copied zallet from the artifact root, never zallet-zaino. Two later
steps in the same job already assumed it was there:

- "Prepare standalone binary artifact": cp build/${PLATFORM_KEY}/zallet-zaino
- "Seed cargo target tree" (.deb staging): install ... zallet-zaino

Both failed with "No such file or directory" the first time the
dual-backend pipeline ran end-to-end (re-release of v0.1.0-alpha.4,
zcash/zallet run 28603304440, job "Build and deploy Zallet
linux-arm64 binaries and packages").

Add the missing install + a matching sanity check, mirroring the
existing zallet check.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Building with --features zaino fails due to default zebra-state feature conflict

3 participants