Skip to main content

Validator Guide

This guide covers everything needed to run a Specter validator, from initial setup to ongoing maintenance, slashing parameters, and key operational commands. Validators are responsible for proposing and signing blocks, and they earn staking rewards in return for securing the network.

Prerequisites

Before becoming a validator, you must have a fully synced Specter full node. Complete the Node Setup guide first and confirm your node is caught up:

umbralined status 2>&1 | jq '.SyncInfo.catching_up'
# Must return: false

Hardware Requirements

Validators have stricter requirements than full nodes due to the real-time demands of consensus:

ResourceMinimumRecommended
CPU4 cores8+ cores (high single-thread performance)
RAM16 GB32 GB
Storage500 GB NVMe SSD1 TB NVMe SSD
Network100 Mbps, low latency1 Gbps, under 50ms to peers
Uptime99%+99.9%+

Software Requirements

  • Go 1.21+
  • umbralined binary (built from source or official release)
  • jq for JSON processing

Stake Requirements

To create a validator, you need a minimum self-delegation of GHOST tokens. The exact minimum depends on the current active set size, but you need enough stake to be in the top N validators by total stake (including delegations).

Create a Validator

Step 1: Create or Import a Key

# Create a new key
umbralined keys add my-validator-key

# OR import an existing mnemonic
umbralined keys add my-validator-key --recover

Save the mnemonic phrase securely. This is the only way to recover your key. Loss of the mnemonic means permanent loss of access to your validator and all staked funds.

# Verify your key was created
umbralined keys show my-validator-key -a
# Returns: umbra1...

# Show the validator's public key (needed for create-validator tx)
umbralined tendermint show-validator
# Returns: {"@type":"/cosmos.crypto.ed25519.PubKey","key":"..."}

Step 2: Fund Your Account

Your validator account needs GHOST to cover the self-delegation and transaction fees.

# Check your balance
umbralined query bank balances $(umbralined keys show my-validator-key -a)

On testnet, use the faucet at the Specter Discord or request tokens from the faucet address.

Step 3: Create the Validator

umbralined tx staking create-validator \
--amount=1000000000000000000000aghost \
--pubkey=$(umbralined tendermint show-validator) \
--moniker="my-validator" \
--chain-id=umbraline_5446-1 \
--commission-rate="0.10" \
--commission-max-rate="0.20" \
--commission-max-change-rate="0.01" \
--min-self-delegation="1" \
--gas="auto" \
--gas-adjustment=1.5 \
--gas-prices="0.001aghost" \
--from=my-validator-key

Parameter breakdown:

ParameterValueDescription
--amount1000000000000000000000aghostInitial self-delegation (1000 GHOST)
--commission-rate0.1010% commission on delegator rewards
--commission-max-rate0.20Maximum commission rate (can never exceed this)
--commission-max-change-rate0.01Maximum daily commission change (1%/day)
--min-self-delegation1Minimum self-delegation in GHOST
warning

--commission-max-rate and --commission-max-change-rate are set at validator creation and cannot be changed later. Choose values carefully.

Step 4: Verify Your Validator

# Check validator status
umbralined query staking validator $(umbralined keys show my-validator-key --bech val -a)

# Check if your validator is in the active set
umbralined query staking validators --status bonded | grep "my-validator"

# Check your validator's signing status
umbralined query slashing signing-info $(umbralined tendermint show-validator)

Managing Your Validator

Edit Validator

Update your validator's description, commission rate, or other mutable fields:

umbralined tx staking edit-validator \
--moniker="new-moniker" \
--details="Specter validator operated by Example Corp" \
--website="https://example.com" \
--identity="KEYBASE_ID" \
--commission-rate="0.12" \
--from=my-validator-key \
--chain-id=umbraline_5446-1 \
--gas="auto" \
--gas-prices="0.001aghost"

The --identity field should be a Keybase.io PGP key fingerprint (16 characters), which links your validator to a verifiable identity.

Delegate Additional Stake

umbralined tx staking delegate \
$(umbralined keys show my-validator-key --bech val -a) \
500000000000000000000aghost \
--from=my-validator-key \
--chain-id=umbraline_5446-1 \
--gas="auto" \
--gas-prices="0.001aghost"

Withdraw Rewards

# Withdraw staking rewards
umbralined tx distribution withdraw-rewards \
$(umbralined keys show my-validator-key --bech val -a) \
--from=my-validator-key \
--chain-id=umbraline_5446-1 \
--gas="auto" \
--gas-prices="0.001aghost"

# Withdraw rewards AND commission
umbralined tx distribution withdraw-rewards \
$(umbralined keys show my-validator-key --bech val -a) \
--commission \
--from=my-validator-key \
--chain-id=umbraline_5446-1 \
--gas="auto" \
--gas-prices="0.001aghost"

Unjail a Validator

If your validator is jailed (due to downtime or other infractions), you can unjail after the jail period expires:

# Check jail status
umbralined query slashing signing-info $(umbralined tendermint show-validator)

# Unjail (after the minimum jail duration has passed)
umbralined tx slashing unjail \
--from=my-validator-key \
--chain-id=umbraline_5446-1 \
--gas="auto" \
--gas-prices="0.001aghost"

Slashing

Slashing is the protocol's mechanism for penalizing validators that act against network interests. There are two slashing conditions:

Downtime (Liveness Fault)

A validator is slashed for downtime if they fail to sign a sufficient number of blocks within a rolling window.

ParameterValue
Slash Fraction1% of staked tokens
Jail Duration10 minutes
Signed Blocks Window100 blocks
Minimum Signed Per Window50% (50 of 100 blocks)

How it works:

  1. The chain tracks the last 100 blocks.
  2. If a validator misses more than 50 of those 100 blocks, they are automatically jailed.
  3. 1% of the validator's total stake (including delegator stake) is slashed.
  4. After 10 minutes, the validator can submit an unjail transaction to rejoin the active set.

Common causes of downtime:

  • Node crash or out-of-memory
  • Network partition or connectivity issues
  • Disk full
  • Misconfigured firewall blocking P2P port

Double Signing (Equivocation)

Double signing occurs when a validator signs two different blocks at the same height. This is a severe offense because it can lead to chain forks.

ParameterValue
Slash Fraction5% of staked tokens
Jail DurationPermanent (tombstoned)

How it works:

  1. CometBFT's evidence module detects that a validator produced two conflicting signatures for the same block height.
  2. The validator is immediately jailed and tombstoned — permanently banned from the active validator set.
  3. 5% of the validator's total stake is slashed.
  4. The validator can never unjail. A new validator must be created with a new key.

Common causes of double signing:

  • Running two instances of the same validator simultaneously (e.g., during migration without stopping the old node first)
  • Restoring a validator from backup without ensuring the old instance is fully stopped
  • Using the same priv_validator_key.json on multiple machines
danger

Double signing results in permanent removal from the validator set (tombstoning). There is no recovery. Always ensure only one instance of your validator is running at any time.

Evidence Module

The evidence module is responsible for detecting and processing equivocation (double-sign) evidence. When a CometBFT node receives conflicting votes from the same validator for the same height/round, it packages this as evidence and includes it in the next block.

Key parameters:

ParameterValueDescription
max_age_num_blocks100,000Evidence older than this is discarded
max_age_duration48 hoursTime-based evidence expiry
max_bytes1,048,576Maximum evidence size per block (1 MB)

Evidence is processed at BeginBlock — before any transactions in the block are executed. This ensures slashing happens promptly.

Monitoring

Essential Metrics to Monitor

MetricWhat to WatchAlert Threshold
Block signingConsecutive missed blocks>10 consecutive misses
Peer countNumber of connected peersFewer than 5 peers
Disk usageAvailable storageUnder 20% remaining
Memory usageRAM consumption>80% utilization
Block heightChain sync status>10 blocks behind
Process statusumbralined runningProcess not found

Prometheus Metrics

Enable Prometheus metrics in config.toml:

[instrumentation]
prometheus = true
prometheus_listen_addr = ":26660"

Key Prometheus metrics to track:

# Consensus height
cometbft_consensus_height

# Number of connected peers
cometbft_p2p_peers

# Missed blocks
cometbft_consensus_missing_validators

# Block processing time
cometbft_consensus_block_interval_seconds

# Transaction count
cometbft_consensus_num_txs

Health Check Script

#!/bin/bash
# validator-health-check.sh

NODE_STATUS=$(umbralined status 2>&1)
CATCHING_UP=$(echo "$NODE_STATUS" | jq -r '.SyncInfo.catching_up')
LATEST_HEIGHT=$(echo "$NODE_STATUS" | jq -r '.SyncInfo.latest_block_height')
PEER_COUNT=$(curl -s http://localhost:26657/net_info | jq -r '.result.n_peers')

echo "Height: $LATEST_HEIGHT"
echo "Catching Up: $CATCHING_UP"
echo "Peers: $PEER_COUNT"

if [ "$CATCHING_UP" = "true" ]; then
echo "WARNING: Node is still syncing!"
fi

if [ "$PEER_COUNT" -lt 5 ]; then
echo "WARNING: Low peer count ($PEER_COUNT)"
fi

# Check if validator is jailed
VALIDATOR_ADDR=$(umbralined keys show my-validator-key --bech val -a)
JAILED=$(umbralined query staking validator "$VALIDATOR_ADDR" -o json 2>/dev/null | jq -r '.jailed')
if [ "$JAILED" = "true" ]; then
echo "CRITICAL: Validator is JAILED!"
fi

Backup and Recovery

Critical Files to Back Up

FileLocationPurpose
priv_validator_key.json~/.umbralined/config/Validator signing key (most critical)
node_key.json~/.umbralined/config/P2P identity key
Mnemonic phraseOffline storageAccount recovery

Backup Procedure

# Back up validator key (store securely, preferably offline/encrypted)
cp ~/.umbralined/config/priv_validator_key.json /secure/backup/location/

# Back up node key
cp ~/.umbralined/config/node_key.json /secure/backup/location/

Recovery Procedure

# 1. Set up a new node (follow Node Setup guide)
umbralined init recovery-node --chain-id umbraline_5446-1

# 2. STOP the old validator completely (critical to prevent double signing!)
# Verify the old instance is fully stopped before proceeding

# 3. Restore the validator key
cp /secure/backup/location/priv_validator_key.json ~/.umbralined/config/

# 4. Download genesis and configure peers
# (follow Node Setup guide)

# 5. Sync the node (state sync recommended for speed)

# 6. Verify sync, then the validator will resume signing automatically
danger

Never run two instances with the same priv_validator_key.json simultaneously. This will cause double signing and result in permanent tombstoning. Always fully stop the old instance before starting a new one.

Key Reference Commands

Validator Status

# View your validator info
umbralined query staking validator $(umbralined keys show my-validator-key --bech val -a)

# View all active validators
umbralined query staking validators --status bonded

# View signing info (missed blocks, jail status)
umbralined query slashing signing-info $(umbralined tendermint show-validator)

# View your outstanding rewards
umbralined query distribution rewards $(umbralined keys show my-validator-key -a)

# View your validator's commission
umbralined query distribution commission $(umbralined keys show my-validator-key --bech val -a)

Staking Operations

# Delegate to your own validator
umbralined tx staking delegate <validator-addr> <amount>aghost \
--from=my-validator-key --chain-id=umbraline_5446-1 --gas=auto --gas-prices=0.001aghost

# Redelegate from one validator to another (no unbonding period)
umbralined tx staking redelegate <src-validator> <dst-validator> <amount>aghost \
--from=my-validator-key --chain-id=umbraline_5446-1 --gas=auto --gas-prices=0.001aghost

# Unbond (start unbonding, subject to unbonding period)
umbralined tx staking unbond <validator-addr> <amount>aghost \
--from=my-validator-key --chain-id=umbraline_5446-1 --gas=auto --gas-prices=0.001aghost

Node Operations

# Check node sync status
umbralined status | jq '.SyncInfo'

# View connected peers
curl -s http://localhost:26657/net_info | jq '.result.peers[] | {id: .node_info.id, moniker: .node_info.moniker, remote_ip: .remote_ip}'

# View the latest block
umbralined query block

# Export state (for debugging or migration)
umbralined export --height <height> > state_export.json