Building Vexor¶
Docs-site note (2026-07-11): Part of the Vexor documentation site (Anza-style). This page documents the exact, current way to build the Vexor validator binary, grounded in
build.zigand the deploy pipeline that gates it. There is one build command for a normal deploy; everything else here explains why.
The toolchain — Zig 0.15.2, exactly¶
Vexor is written in Zig 0.15.2. The build does not produce a correct binary on any other version, and this is not a "probably fine" version pin — it is load-bearing:
- Zig 0.15.2 uses the
--release=safe|fast|smallflag form; it rejects the older-Doptimize=…syntax. - The
std.fs.FileReader/Writeroverhaul andArrayListbecoming unmanaged-by-default both landed between 0.14 and 0.15, and Vexor's code depends on the 0.15.2 shape of both. - An older compiler can still link and run a binary — it just silently miscompiles parts of it. That is a worse failure mode than a build error, because nothing tells you it happened.
Check zig version before building. If it doesn't print 0.15.2, install the pinned toolchain rather than
using whatever zig resolves to on $PATH.
The one command¶
Run from the repo root with the pinned Zig 0.15.2. -Dprod is the bundling switch: it turns on all
8 canonical production feature flags at once (the table below). -Dpure_zig is retained on the command
line for compatibility: crypto is now unconditionally pure-Zig (the Firedancer Ballet AVX-512 backend was
removed outright — see why below), so the flag is a no-op,
but passing it costs nothing and keeps the build command self-documenting. Every flag in
build.zig defaults to off, so a bare zig build is a deliberately minimal, fast-iterating binary that is
not what you want to run a validator with — -Dprod -Dpure_zig is what turns a checkout into a deployable
production binary. The bundle is golden-replay-gated as behaviorally identical to enabling all 8 flags
explicitly, and each flag remains individually overridable on top of it (e.g. -Dprod -Dpure_zig
-Dfec_dedup=false) for forensic or bisect builds that need a narrower flag surface.
-Dcpu=znver4 matches Vexor's reference box (an EPYC 9374F) — see Hardware for why. If your
hardware is a different CPU, set -Dcpu= to match (Zig's standard target-CPU option, not something
Vexor-specific); getting it wrong doesn't break consensus, but it can silently disable ISA extensions
(AVX-512, sha_ni, vaes) that sig-verify and hashing lean on for throughput.
The resulting binary lands at zig-out/bin/vex-fd. The deploy tooling additionally self-checks the binary's
compiled-in feature markers before launch, so a mis-built binary fails the deploy rather than silently
shipping — see Deploying Vexor.
What -Dprod bundles¶
-Dprod turns on 8 flags, each of which is individually a -D option in build.zig and defaults to
off there:
| Flag | What it does | Why it matters |
|---|---|---|
leader_mode |
Block-production tick loop | Required to produce blocks as leader (separate from whether broadcast is armed). |
repair_stake_weighting |
Stake-weighted repair-peer selection | Faster, more correct repair versus round-robin peer choice. |
parallel_exec |
Wave-barrier parallel transaction execution | Throughput — parallelizes non-conflicting transactions across a worker pool. |
fec_dedup |
Ed25519 signature dedup across a FEC set | Throughput — avoids re-verifying signatures already checked once. |
watchdog |
In-process liveness-observing thread | Operational — see supervision. |
status_cache |
Cross-block already-processed cache | Needed to safely support transaction-bearing block production. |
use_native_quic_votes |
Route votes over QUIC instead of UDP | Networking — reduces reliance on the UDP+relay vote path. |
History (2026-07-12 and earlier): two other flags used to be part of this bundle and no longer exist.
sig_votebuilt the Sig-derived vote-execution path that served as a differential/shadow oracle while Vexor's own vote program (the July 2026 rewrite) was being validated; that oracle and the flag that built it were deleted outright in Stage 8 (2026-07-12) once the rewrite's own KAT/carrier/golden-replay gates were sufficient on their own — voteforge is now the sole, unconditional vote executor, with no flag to select an alternative.ballet_bn254(and its siblingsballet_ed25519/ballet_blake3) linked Firedancer's Ballet AVX-512 crypto library; that backend was fully removed frombuild.zig(not merely overridden) once the pure-Zig crypto rewrite reached parity — see below.
-Dvex_ledger (included in -Dprod) adds the persistent blockstore on top of this set:
| Flag | What it does | Why it matters |
|---|---|---|
vex_ledger |
Vexor's own Zig-native blockstore | Enables the VexLedger storage path. |
Two other feature flags default on in build.zig regardless of which script you use and don't need to be
passed: jemalloc (the allocator) and two_tier (the accounts-DB read/commit design). Everything outside
these lists is either a default-on baseline or an unvetted, experimental feature gated behind its own flag —
The -Dprod option's doc comment in build.zig enumerates what it deliberately still omits and why. Turning one of
those on outside of a deliberate test is not part of a normal build.
A handful of the bundled flags are also gated behind a runtime environment variable at deploy time — building the capability in and arming it at runtime are two separate switches, so a production binary with no env set behaves identically to a binary built without that flag at all. See the Environment Variable Reference for which ones.
The crypto backend: Firedancer Ballet vs. pure-Zig¶
Vexor originally linked Firedancer's ballet AVX-512 C libraries for the leaf-level crypto primitives
(Ed25519, BLAKE3, BN254/alt_bn128, Poseidon) behind three flags — ballet_ed25519, ballet_blake3,
ballet_bn254. That backend has since been removed from build.zig outright (not just defaulted off):
once the pure-Zig rewrite reached parity, the removal was proven behavior-inert (the -Dprod -Dpure_zig
binary's .text/.rodata/.data sections came out byte-identical to the pre-removal baseline) and the dead
Ballet flags, externs, and linked .a artifacts were deleted. There is no build configuration that links
Firedancer crypto code anymore — ballet_* are not valid -D flags on any current checkout.
Crypto is now unconditionally pure-Zig, Vexor's own from-scratch implementation of the same primitives
(src/vex_crypto/) — the binary links zero Firedancer symbols at runtime, always. -Dpure_zig is kept
as an accepted flag on the build command for compatibility and self-documentation, but it is a no-op: zig
build -Dprod alone now produces the same pure-Zig binary as -Dprod -Dpure_zig. The pure-Zig crypto is
verified byte-exact against known-answer test vectors (including published solana-bn254 / EIP-197 /
go-ethereum vectors for BN254) and against live cluster parity. See
NOTICE and PROVENANCE for the attribution story and
The Vote Program for how the crypto rewrite fits into the broader
Firedancer-independence effort.
Every binary still needs one runtime guard: VEX_ALLOW_NO_BN254=1. The BN254 boot guard historically
assumed a linked Ballet BN254 library and refuses to boot without it; since no build links that library
anymore, this environment variable is required on every deploy, unconditionally — not just for a subset of
builds. See the Environment Variable Reference for details.
Why --release=safe¶
Zig's optimize modes trade off speed, size, and runtime safety checks. --release=safe is optimized code
with bounds checks, integer-overflow checks, and similar safety nets still compiled in — it is the mode a
validator should run in:
- A Debug build is roughly 30× slower. A validator in Debug mode falls behind the cluster tip and can't keep up with replay, sig-verify, or PoH hashing in real time.
--release=faststrips the safety checks entirely. For a process executing untrusted, adversarial input (arbitrary on-chain transactions) at the center of a consensus system, that is the wrong trade — a checked crash is vastly preferable to unchecked memory corruption.
--release=safe is the canonical release mode for every Vexor build, always paired with the full production
flag set (-Dprod) for a production deploy.
Binary size as a sanity check¶
A correctly built --release=safe binary is on the order of 15–17 MB. A binary over 50 MB is almost
certainly an accidental Debug build — the deploy pipeline treats that as a hard warning sign and refuses to
launch it unless explicitly overridden, precisely because a Debug binary silently fails to keep up with the
cluster rather than failing loudly. If your build comes out far larger than that, check that --release=safe
was actually passed.
Verifying the flags landed¶
Because dropping a single flag produces a binary that runs and votes but silently diverges from the cluster under specific conditions, don't just trust that the build command was typed correctly — verify it landed. The deploy tooling already does this for you: it runs a self-check after every build that greps the output binary for each flag's compiled-in marker string and fails the build if any is missing, rather than letting a silently-incomplete binary reach the deploy step. The deploy pipeline then runs its own, narrower version of the same check before it will launch a binary (see Consensus guards). You can run the same kind of check manually at any time:
strings -n6 zig-out/bin/vex-fd | grep -c voteforge # Vexor vote program present?
strings -n6 zig-out/bin/vex-fd | grep -c fd_bn254 # expect 0 — no Firedancer BN254 linked, ever
A nonzero voteforge count confirms the vote program is in the binary you're about to run — it is the only
vote-execution path there is now, so this should always be nonzero. The fd_bn254 count should always read
zero: every current build is crypto-unconditionally pure-Zig, so there is no configuration that links
Firedancer's Ballet BN254 library anymore.
Build while the node is stopped¶
Building a release binary is CPU-intensive across every core on the box. If a validator is currently voting on the same machine, build after stopping it, not concurrently — this removes any question of the build contending with the consensus-critical replay/vote path for CPU or I/O. See the deploy contract for the stop → build → deploy sequence.
Quick reference¶
# Confirm the toolchain
zig version # must print 0.15.2
# Build the production binary (set -Dcpu= to your target CPU)
zig build -Dprod -Dpure_zig -Dcpu=znver4 --release=safe # the bundle is golden-gated; deploy self-checks the markers
# Sanity-check the output
ls -la zig-out/bin/vex-fd # ~15-17 MB, not 50+ MB
strings -n6 zig-out/bin/vex-fd | grep -c voteforge # Vexor vote program compiled in (expect >0)
strings -n6 zig-out/bin/vex-fd | grep -c fd_bn254 # Firedancer BN254 linked (expect 0 for -Dpure_zig)
# Then deploy — see the Deploy page (pure-Zig binaries need VEX_ALLOW_NO_BN254=1 at launch)