Node Setup
This guide walks through setting up a Specter full node from scratch. A full node validates all transactions and blocks, maintains a complete copy of the blockchain state, and can serve RPC queries. Validators should start here before proceeding to the Validator Guide.
Hardware Requirements
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 4 cores | 8+ cores |
| RAM | 16 GB | 32 GB |
| Storage | 500 GB SSD | 1 TB NVMe SSD |
| Network | 100 Mbps | 1 Gbps |
| OS | Ubuntu 22.04 LTS / macOS 13+ | Ubuntu 22.04 LTS |
Storage requirements grow over time as the chain accumulates state. NVMe SSDs are strongly recommended for validators due to the I/O demands of CometBFT consensus and EVM state access.
Software Prerequisites
| Software | Version | Purpose |
|---|---|---|
| Go | 1.21+ | Building umbralined from source |
| Git | 2.x | Cloning the repository |
| Make | GNU Make | Build automation |
| jq | 1.6+ | JSON processing for scripts |
Install Go
# Download and install Go 1.21+
wget https://go.dev/dl/go1.21.6.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.21.6.linux-amd64.tar.gz
# Add to PATH (add to ~/.bashrc or ~/.zshrc for persistence)
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
# Verify
go version
# go version go1.21.6 linux/amd64
Build from Source
# Clone the repository
git clone https://github.com/specter-chain/umbraline-cosmos.git
cd umbraline-cosmos
# Build the binary
make install
# Verify installation
umbralined version
The make install command compiles the umbralined binary and places it in $GOPATH/bin.
Initialize the Node
# Set your node's moniker (human-readable name)
MONIKER="my-specter-node"
# Initialize the node
umbralined init $MONIKER --chain-id umbraline_5446-1
This creates the following directory structure at ~/.umbralined/:
~/.umbralined/
├── config/
│ ├── app.toml # Application configuration (EVM, API, gas)
│ ├── config.toml # CometBFT configuration (P2P, RPC, consensus)
│ ├── genesis.json # Genesis state (will be replaced)
│ ├── node_key.json # P2P identity key
│ └── priv_validator_key.json # Validator signing key (BACK THIS UP)
└── data/
└── priv_validator_state.json # Validator state tracking
Configure Genesis
Replace the auto-generated genesis file with the official Specter genesis:
# Download the official genesis file
curl -L https://raw.githubusercontent.com/specter-chain/network-config/main/testnet/genesis.json \
-o ~/.umbralined/config/genesis.json
# Verify the genesis hash
sha256sum ~/.umbralined/config/genesis.json
# Expected: <check official documentation for current hash>
The genesis file contains the initial chain state, including the genesis allocation of 1 billion GHOST tokens distributed across the treasury, operator, faucet, and validator accounts.
Configure Peers
Your node needs peers to discover the network. Add seed nodes and persistent peers to your configuration.
Seeds
Seeds are nodes that provide initial peer discovery. Your node contacts them once to learn about other peers, then disconnects.
# Set seed nodes
SEEDS="<seed-node-id>@seeds.specterchain.com:26656"
sed -i "s/^seeds =.*/seeds = \"$SEEDS\"/" ~/.umbralined/config/config.toml
Persistent Peers
Persistent peers are nodes your node maintains a constant connection to.
# Set persistent peers
PEERS="<peer-id-1>@peer1.specterchain.com:26656,<peer-id-2>@peer2.specterchain.com:26656"
sed -i "s/^persistent_peers =.*/persistent_peers = \"$PEERS\"/" ~/.umbralined/config/config.toml
Check the Specter Discord or the network-config repository for the latest peer list.
Configure Minimum Gas Prices
Set a minimum gas price to protect against spam transactions:
sed -i 's/^minimum-gas-prices =.*/minimum-gas-prices = "0.001aghost"/' ~/.umbralined/config/app.toml
Enable the JSON-RPC Server
If you want your node to serve EVM JSON-RPC queries (required for Web3 applications):
# In app.toml, enable the JSON-RPC server
sed -i '/\[json-rpc\]/,/\[/ s/^enable =.*/enable = true/' ~/.umbralined/config/app.toml
sed -i '/\[json-rpc\]/,/\[/ s/^address =.*/address = "0.0.0.0:8545"/' ~/.umbralined/config/app.toml
sed -i '/\[json-rpc\]/,/\[/ s/^ws-address =.*/ws-address = "0.0.0.0:8546"/' ~/.umbralined/config/app.toml
Start the Node
Direct Execution
umbralined start
Systemd Service (Recommended for Production)
Create a systemd service file for automatic restarts and log management:
sudo tee /etc/systemd/system/umbralined.service > /dev/null <<EOF
[Unit]
Description=Specter Node
After=network-online.target
[Service]
User=$USER
ExecStart=$(which umbralined) start
Restart=on-failure
RestartSec=3
LimitNOFILE=65535
Environment="DAEMON_HOME=$HOME/.umbralined"
Environment="DAEMON_NAME=umbralined"
[Install]
WantedBy=multi-user.target
EOF
# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable umbralined
sudo systemctl start umbralined
Viewing Logs
# Follow logs in real time
sudo journalctl -u umbralined -f
# View last 100 lines
sudo journalctl -u umbralined -n 100 --no-pager
Check Sync Status
# Check if the node is catching up
umbralined status | jq '.SyncInfo'
Expected output while syncing:
{
"latest_block_hash": "A1B2C3...",
"latest_app_hash": "D4E5F6...",
"latest_block_height": "123456",
"latest_block_time": "2026-03-16T00:00:00.000000000Z",
"earliest_block_hash": "000000...",
"earliest_app_hash": "",
"earliest_block_height": "1",
"earliest_block_time": "2025-01-01T00:00:00Z",
"catching_up": true
}
When catching_up changes to false, your node is fully synced and following the chain head.
Quick Sync Check
# One-liner to check sync status
umbralined status 2>&1 | jq '.SyncInfo.catching_up'
# Returns: true (still syncing) or false (fully synced)
Check Connected Peers
# View the number of connected peers
umbralined status 2>&1 | jq '.NodeInfo.network'
curl -s http://localhost:26657/net_info | jq '.result.n_peers'
State Sync (Fast Sync)
For faster initial sync, you can use state sync to download a recent snapshot of the chain state instead of replaying all blocks from genesis.
# Get a recent trusted block height and hash from a trusted RPC node
LATEST_HEIGHT=$(curl -s https://testnet.specterchain.com:26657/block | jq -r '.result.block.header.height')
TRUST_HEIGHT=$((LATEST_HEIGHT - 2000))
TRUST_HASH=$(curl -s "https://testnet.specterchain.com:26657/block?height=$TRUST_HEIGHT" | jq -r '.result.block_id.hash')
echo "Trust Height: $TRUST_HEIGHT"
echo "Trust Hash: $TRUST_HASH"
# Configure state sync in config.toml
sed -i '/\[statesync\]/,/\[/ s/^enable =.*/enable = true/' ~/.umbralined/config/config.toml
sed -i "/\[statesync\]/,/\[/ s|^rpc_servers =.*|rpc_servers = \"https://testnet.specterchain.com:26657,https://testnet.specterchain.com:26657\"|" ~/.umbralined/config/config.toml
sed -i "/\[statesync\]/,/\[/ s/^trust_height =.*/trust_height = $TRUST_HEIGHT/" ~/.umbralined/config/config.toml
sed -i "/\[statesync\]/,/\[/ s/^trust_hash =.*/trust_hash = \"$TRUST_HASH\"/" ~/.umbralined/config/config.toml
After configuring state sync, restart the node:
# Reset the data directory (preserves config)
umbralined tendermint unsafe-reset-all --home ~/.umbralined --keep-addr-book
# Start the node
sudo systemctl restart umbralined
State sync only provides a snapshot of the recent state. If you need full historical data (e.g., for an indexer or archive node), you must sync from genesis.
Troubleshooting
Node won't start
# Check for port conflicts
sudo lsof -i :26656 # P2P
sudo lsof -i :26657 # Tendermint RPC
sudo lsof -i :8545 # EVM JSON-RPC
sudo lsof -i :1317 # REST API
sudo lsof -i :9090 # gRPC
No peers found
- Verify your firewall allows inbound/outbound on port
26656. - Confirm seed/peer addresses are correct and reachable.
- Check that your
config.tomlhas the correctexternal_addressset if behind NAT.
# Set external address if behind NAT
EXTERNAL_IP=$(curl -s https://ifconfig.me)
sed -i "s/^external_address =.*/external_address = \"tcp:\/\/$EXTERNAL_IP:26656\"/" ~/.umbralined/config/config.toml
Node is stuck at a block height
This typically indicates a consensus issue. Check logs for error messages:
sudo journalctl -u umbralined -n 200 --no-pager | grep -i "error\|ERR\|panic"
If the node is stuck due to an app hash mismatch, you may need to reset state and resync:
umbralined tendermint unsafe-reset-all --home ~/.umbralined --keep-addr-book
sudo systemctl restart umbralined