VexVisor -- Upgrade Governance Module
VexVisor is the on-chain upgrade governance module (discriminants 46-48) that enables coordinated binary upgrades across the validator set without manual intervention.
How It Works
- Schedule -- Authority proposes upgrade: name, target height, binary URL, SHA256 checksum
- Vote -- Validators vote to approve/reject (stake-weighted, 67% quorum in
fullmode) - Halt -- At target height, node writes
data/upgrade-info.jsonand exits with code 42 - VexVisor wrapper --
tools/vexvisor.shdetects exit(42), downloads new binary, verifies SHA256, restarts. Auto-rollback if crash within 60s.
Governance Modes
| Mode | Behavior | Use Case |
|---|---|---|
seed-authority (default) | Upgrade authority auto-approves, no voting needed | Early testnet |
full | Stake-weighted voting with configurable quorum (default 67%) | Public beta / mainnet |
Configure with --governance-mode <seed-authority|full>.
Operations
Schedule an Upgrade
Proposes a new upgrade to the network. The --from flag must use the upgrade authority's pubkey (not the Blake3 address).
vexidus upgrade schedule \
--rpc http://localhost:9933 \
--from 0x782e29c3...babe \
--name "lthash-audit-fix" \
--height 150400 \
--binary-url "https://vexidus.io/releases/vexidus-node" \
--checksum "1942342e82ca5616d309b016e6cb63bcc7a78adbd3af66211847d9cbf4c02084"
Requirements:
- Target height must be at least 100 blocks in the future
- Only one active upgrade plan at a time
- In
seed-authoritymode, must be signed by the upgrade authority - Gas: 80,000
Vote on an Upgrade
Validators vote to approve or reject a proposed upgrade (only in full mode).
vexidus upgrade vote --name "v0.2.0" --approve
- Stake-weighted voting
- Double-vote prevention (each validator votes once)
- Quorum: 67% by default (configurable via
--upgrade-quorum) - Gas: 40,000
Cancel an Upgrade
Cancel a scheduled upgrade before it executes.
vexidus upgrade cancel --name "v0.2.0"
- Only the upgrade authority can cancel
- Gas: 60,000
View Active Plan
vexidus upgrade plan
Pre-stage a Binary
Download and verify the upgrade binary before the target height is reached. This eliminates download latency during the actual upgrade.
vexidus upgrade prepare --rpc http://localhost:9933
The binary is downloaded, checksum-verified, and stored in vexvisor/upgrades/<name>/. VexVisor will use the pre-staged binary at upgrade height instead of downloading.
Emergency Rollback
Interactive rollback that lists available backup binaries and lets you select which to restore:
vexidus upgrade rollback
The command finds all .pre-* backup binaries (created automatically by VexVisor on each upgrade), displays them sorted by date, and performs the swap.
RPC Methods
| Method | Description |
|---|---|
vex_scheduleUpgrade | Schedule a network upgrade |
vex_cancelUpgrade | Cancel a scheduled upgrade |
vex_voteUpgrade | Vote on a scheduled upgrade |
vex_getUpgradePlan | View the active upgrade plan |
The VexVisor Wrapper
In production, all validators run via the tools/vexvisor.sh wrapper script. This script:
- Monitors exit codes -- exit(42) means "upgrade required"
- Downloads the new binary from the URL specified in the upgrade plan (or uses a pre-staged binary if available)
- Verifies SHA256 checksum -- rejects tampered binaries
- Ed25519 signature verification (Tier 1) -- optionally verifies the binary is signed by the upgrade authority's Ed25519 key. Enable with
--require-signed-upgrades - Restarts with new binary -- automatic, no manual intervention
- Auto-rollback -- if the new binary crashes within 60 seconds, rolls back to the previous version
- Exponential backoff -- retries with increasing delays on repeated failures
Upgrade Lifecycle
Schedule Upgrade (RPC/CLI)
|
v
Voting Period (full mode only)
| 67% stake-weighted quorum required
v
Approved -> Target Height Reached
|
v
Node writes upgrade-info.json + exits(42)
|
v
VexVisor downloads binary + SHA256 verify
|
v
Restart with new binary
|
v
Grace Period (100 blocks) -- no jailing during upgrade
Upgrade Grace Period
After an upgrade halt, a grace period of 100 blocks (~200 seconds) suppresses missed-block tracking. This prevents validators from being jailed simply for performing an upgrade.
Expiry
Upgrade proposals that are not approved before voting_ends_at_height are automatically cancelled via expire_upgrade_voting().
CLI Flags
| Flag | Default | Description |
|---|---|---|
--governance-mode | seed-authority | Governance mode (seed-authority or full) |
--upgrade-authority | none | Hex pubkey of the upgrade authority |
--voting-period | 500 | Voting period in blocks |
--upgrade-quorum | 67 | Required approval quorum (percent) |
--auto-vote-seed | none | Auto-vote seed for deterministic voting |
Track Record
VexVisor has been proven in production with 30+ successful upgrades across 5 validators, zero manual intervention:
| # | Name | Block | What Changed |
|---|---|---|---|
| 1-5 | Early testnet | Various | VexBridge v3, metadata, bridge map registry |
| 6 | bridge-map-registry | 58,250 | Canonical bridge map |
| 7 | bridge-map-nonce-fix | 59,300 | Foundation nonce fix |
| 8 | dragonfly-stream | 91,323 | Mempoolless PQ-sealed pipeline |
| 9 | lthash-state-root | 132,950 | LtHash homomorphic state root |
| 10 | lthash-audit-fix | 150,400 | 6 LtHash bypass fixes + sync asymmetry |
| 11 | delegation-rpc | 161,200 | Delegation, pool rewards, auto-compound |
| 12 | dragonfly-phase3 | 200,551 | Mempool eliminated, sealed forwarding |
| 13 | p2p-auth | 217,748 | Validator identity + direct-to-leader |
| 14-15 | Goal + case fixes | Various | Goal::Transfer, case-insensitive ops |
| 16 | vexidex-rebuild | ~300K | DEX AMM rebuild + adaptive block timing |
| 17-18 | Reward split + LtHash | Various | Foundation 15/85 split, LtHash race fix |
| 19-21 | Risk management | Various | Freeze, circuit breaker, state correction |
| 22 | state-preserving-genesis | ~515,919 | Export/import genesis restart |
| 23 | intent-decimals | ~816,300 | Per-token decimals in intent parser |
| 24 | ws-subscriptions | ~914,000 | WebSocket subscriptions + leader skip |
All upgrades: auto-halt at target height, SHA256-verified download, automatic restart. Average downtime per upgrade: ~3 seconds.
Code Signing (Tier 1)
VexVisor supports Ed25519 code signing for upgrade binaries. When enabled, validators reject binaries that are not signed by the upgrade authority's key.
How it works:
- The upgrade authority signs the binary with their Ed25519 key:
vexidus upgrade sign --binary vexidus-node --key authority.hex - The signature is included in the upgrade schedule transaction
- VexVisor verifies the signature before accepting the binary
CLI flags:
| Flag | Description |
|---|---|
--require-signed-upgrades | Reject unsigned upgrade binaries |
--upgrade-authority | Hex pubkey of the signing authority |
This is Tier 1 of the code signing roadmap. Future tiers will add multi-key signing (M-of-N validator committee) and reproducible build attestation.
Auto-Claim Rewards
Validators can set up automatic reward claiming to ensure they always have VXS for gas fees. A cron job runs the tools/claim-rewards.sh script daily:
# Daily at 06:00 UTC
0 6 * * * /path/to/claim-rewards.sh \
--validator "0xYOUR_VALIDATOR_ADDRESS" \
--rpc http://localhost:9933 \
>> /var/log/vexidus-claim.log 2>&1
The script checks pending rewards via vex_getValidator, and if rewards are available, calls vex_claimRewards to collect them into the validator's balance.
Evolution Plan
- Tier 1 (Current): Seed-authority mode with Ed25519 code signing. 50+ upgrades deployed successfully.
- Tier 2: Full mode with auto-vote from seed -- automated governance
- Tier 3: Real stake-weighted voting with manual validator decisions + multi-key code signing
- Tier 4: VSC-88 protocol governance (parameter changes, treasury, multi-sig) -- LIVE + VSC-99 DAO program (token-weighted community governance) -- LIVE
SDK
use vexidus_sdk::BundleBuilder;
// Schedule an upgrade
let bundle = BundleBuilder::new(&sender)?
.schedule_upgrade(name, height, url, checksum, info_url)
.sign(&wallet);
// Cancel an upgrade
let bundle = BundleBuilder::new(&sender)?
.cancel_upgrade(name)
.sign(&wallet);
// Vote on an upgrade
let bundle = BundleBuilder::new(&sender)?
.vote_upgrade(name, approve)
.sign(&wallet);