Worrell
Manual node installation
Worrell reviewed manual node installation and validator workflow.
Configure once โ generate every command
These values update the Worrell installation, wallet, validator, sync and firewall commands below.
Advanced node settings
Optional pruning, indexer and validator defaults.
Network: Testnet
Chain ID: worrell-testnet-1
Guide review: 2026-09-08 ยท 5 source references
Reviewed binary version: v0.1.2
Recommended hardware: 2 CPU, 4GB RAM and 100GB SSD minimum
Binary: worrelld
Node home: ~/.worrell
Prepare the node host
# install dependencies
sudo apt update
sudo apt install -y ca-certificates curl wget jq lz4 tar unzip
# This network uses a reviewed binary release; Go is not required.Set node variables
# set variables
ENV_FILE="$HOME/.bonynode-worrell.env"
cat > "$ENV_FILE" <<'EOF'
export WALLET='wallet'
export MONIKER='validator-name'
export WORRELL_CHAIN_ID='worrell-testnet-1'
export WORRELL_PORT='51'
export WORRELL_HOME="$HOME/.worrell"
export DAEMON_NAME='worrelld'
EOF
chmod 600 "$ENV_FILE"
touch "$HOME/.bash_profile"
PROFILE_LINE='source "$HOME/.bonynode-worrell.env"'
grep -qxF "$PROFILE_LINE" "$HOME/.bash_profile" || echo "$PROFILE_LINE" >> "$HOME/.bash_profile"
source "$ENV_FILE"
printf 'Wallet: %s | Moniker: %s | Chain ID: %s | Port prefix: %s\n' \
"$WALLET" "$MONIKER" "$WORRELL_CHAIN_ID" "$WORRELL_PORT"Install the checksum-verified node binary
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
VERSION="v0.1.2"
ARCHIVE="$HOME/worrell-v0.1.2-linux-amd64.tar.gz"
EXTRACT_DIR="$HOME/worrell-v0.1.2-linux-amd64"
ARTIFACT_URL="https://github.com/worrellchain/worrell/releases/download/v0.1.2/v0.1.2_linux_amd64.tar.gz"
EXPECTED_SHA256="1542bc561227ecd6bfb2d35f3bc81b8fd34e6f6db1404923b5bf5bd4b8303cd2"
test "$(uname -m)" = "x86_64" || { echo "This reviewed artifact supports x86_64 only." >&2; exit 1; }
test ! -e "$ARCHIVE" || { echo "Stop: $ARCHIVE already exists; review it first." >&2; exit 1; }
test ! -e "$EXTRACT_DIR" || { echo "Stop: $EXTRACT_DIR already exists; review it first." >&2; exit 1; }
mkdir -m 0750 "$EXTRACT_DIR"
curl --fail --show-error --location --retry 3 --proto "=https" --proto-redir "=https" --output "$ARCHIVE" "$ARTIFACT_URL"
printf '%s %s\n' "$EXPECTED_SHA256" "$ARCHIVE" | sha256sum --check -
tar --no-same-owner --no-same-permissions -xzf "$ARCHIVE" -C "$EXTRACT_DIR"
test -x "$EXTRACT_DIR/worrelld"
sudo install -m 0755 "$EXTRACT_DIR/worrelld" "/usr/local/bin/worrelld"
"worrelld" version --long | grep -E 'v?0\.1\.2|2d849e1a33f6204952412a14e7b12cb5ef3870dd'
# Verify which binary will be used before initialization.
command -v "worrelld"
"worrelld" version
)Initialize a new node home
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
BIN="worrelld"
CHAIN_ID="worrell-testnet-1"
MONIKER='validator-name'
NODE_HOME="$HOME/.worrell"
command -v "$BIN"
test ! -e "$NODE_HOME" || { echo "Stop: $NODE_HOME already exists; back it up and review it first." >&2; exit 1; }
"$BIN" init "$MONIKER" --chain-id "$CHAIN_ID" --home "$NODE_HOME"
test -f "$NODE_HOME/config/genesis.json"
set -euo pipefail
CHAIN_ID="worrell-testnet-1"
NODE_HOME="$HOME/.worrell"
CLIENT_TOML="$NODE_HOME/config/client.toml"
LOCAL_RPC="tcp://127.0.0.1:51657"
test -f "$CLIENT_TOML"
cp --no-clobber "$CLIENT_TOML" "$CLIENT_TOML.bonynode-backup" || true
sed -i -E \
-e "s|^chain-id[[:space:]]*=.*|chain-id = \"$CHAIN_ID\"|" \
-e 's|^keyring-backend[[:space:]]*=.*|keyring-backend = "os"|' \
-e "s|^node[[:space:]]*=.*|node = \"$LOCAL_RPC\"|" \
"$CLIENT_TOML"
grep -E '^(chain-id|keyring-backend|node)[[:space:]]*=' "$CLIENT_TOML"
)Install genesis and configure peers
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
CHAIN_ID='worrell-testnet-1'
NODE_HOME="$HOME/.worrell"
GENESIS_URL='https://testnet-files.bonynode.online/worrell/genesis.json'
GENESIS_SHA256='a81c507b12ba0678c3172394ff4bb03e1c3db60050cc5568c127a24ec19378fd'
GENESIS_MAX_BYTES=1000000
WORK_DIR=$(mktemp -d)
trap 'rm -rf "$WORK_DIR"' EXIT
GENESIS_DOWNLOAD="$WORK_DIR/genesis.download"
GENESIS_JSON="$WORK_DIR/genesis.json"
GENESIS_CHUNK="$WORK_DIR/genesis-chunk-zero.json"
test -d "$NODE_HOME/config"
curl --fail --show-error --location --retry 3 \
--connect-timeout 15 --max-time 1800 --proto '=https' --proto-redir '=https' \
--max-filesize "$GENESIS_MAX_BYTES" --output "$GENESIS_JSON" "$GENESIS_URL"
jq -e --arg chain "$CHAIN_ID" 'type == "object" and .chain_id == $chain and (.app_state | type == "object")' "$GENESIS_JSON" >/dev/null
printf '%s %s\n' "$GENESIS_SHA256" "$GENESIS_JSON" | sha256sum --check -
cp --no-clobber "$NODE_HOME/config/genesis.json" "$NODE_HOME/config/genesis.json.init-backup" || true
install -m 0644 "$GENESIS_JSON" "$NODE_HOME/config/genesis.json"
printf 'Installed verified genesis for %s (%s)\n' "$CHAIN_ID" "$GENESIS_SHA256"
set -euo pipefail
NODE_HOME="$HOME/.worrell"
ADDRBOOK_URL='https://testnet-files.bonynode.online/worrell/addrbook.json'
ADDRBOOK_SHA256='d3eb70d68cc9c56c7a5d7b3bbb32b599a25987f0804f56d4eefdb9a8138bb9de'
ADDRBOOK_MAX_BYTES=1000000
WORK_DIR=$(mktemp -d)
trap 'rm -rf "$WORK_DIR"' EXIT
ADDRBOOK_JSON="$WORK_DIR/addrbook.json"
test -d "$NODE_HOME/config"
curl --fail --show-error --location --retry 3 --connect-timeout 15 --max-time 120 --proto '=https' --proto-redir '=https' --max-filesize "$ADDRBOOK_MAX_BYTES" --output "$ADDRBOOK_JSON" "$ADDRBOOK_URL"
jq -e 'type == "object" and (.addrs | type == "array") and (.addrs | length > 0)' "$ADDRBOOK_JSON" >/dev/null
printf '%s %s
' "$ADDRBOOK_SHA256" "$ADDRBOOK_JSON" | sha256sum --check -
cp --no-clobber "$NODE_HOME/config/addrbook.json" "$NODE_HOME/config/addrbook.json.init-backup" || true
install -m 0644 "$ADDRBOOK_JSON" "$NODE_HOME/config/addrbook.json"
printf 'Installed reviewed addrbook (%s peers, %s)
' 20 "$ADDRBOOK_SHA256"
set -euo pipefail
CONFIG_TOML="$HOME/.worrell/config/config.toml"
PERSISTENT_PEERS='[email protected]:26656,40128ea31b1cfb5d4b24fc9e32ee0c468586c983@worrell-testnet-peer.itrocket.net:12656'
test -f "$CONFIG_TOML"
cp --no-clobber "$CONFIG_TOML" "$CONFIG_TOML.bonynode-backup" || true
sed -i "s|^persistent_peers *=.*|persistent_peers = \"$PERSISTENT_PEERS\"|" "$CONFIG_TOML"
grep '^persistent_peers' "$CONFIG_TOML"
)Apply ports, pruning, gas and network settings
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
NODE_HOME="$HOME/.worrell"
APP_TOML="$NODE_HOME/config/app.toml"
CONFIG_TOML="$NODE_HOME/config/config.toml"
test -f "$APP_TOML"
test -f "$CONFIG_TOML"
cp --no-clobber "$APP_TOML" "$APP_TOML.bonynode-backup" || true
cp --no-clobber "$CONFIG_TOML" "$CONFIG_TOML.bonynode-backup" || true
# Application API, gRPC and optional EVM ports.
sed -i \
-e 's|:1317|:51317|g' \
-e 's|:8080|:51080|g' \
-e 's|:9090|:51090|g' \
-e 's|:9091|:51091|g' \
-e 's|:8545|:51545|g' \
-e 's|:8546|:51546|g' \
-e 's|:6065|:51065|g' \
"$APP_TOML"
# CometBFT RPC, P2P, proxy-app, pprof and Prometheus ports.
sed -i \
-e 's|:26658|:51658|g' \
-e 's|:26657|:51657|g' \
-e 's|:6060|:51060|g' \
-e 's|:26656|:51656|g' \
-e 's|:26660|:51660|g' \
"$CONFIG_TOML"
sed -i -E \
-e 's|^pruning[[:space:]]*=.*|pruning = "custom"|' \
-e 's|^pruning-keep-recent[[:space:]]*=.*|pruning-keep-recent = "100"|' \
-e 's|^pruning-keep-every[[:space:]]*=.*|pruning-keep-every = "0"|' \
-e 's|^pruning-interval[[:space:]]*=.*|pruning-interval = "19"|' \
"$APP_TOML"
sed -i -E 's|^indexer[[:space:]]*=.*|indexer = "kv"|' "$CONFIG_TOML"
sed -i -E 's|^prometheus[[:space:]]*=.*|prometheus = true|' "$CONFIG_TOML"
grep -E '^(pruning|pruning-keep-recent|pruning-interval)[[:space:]]*=' "$APP_TOML"
grep -E '^(indexer|prometheus)[[:space:]]*=' "$CONFIG_TOML"
printf 'RPC=%s P2P=%s API=%s gRPC=%s Prometheus=%s\n' \
'51657' '51656' '51317' \
'51090' '51660'
set -euo pipefail
APP_TOML="$HOME/.worrell/config/app.toml"
test -f "$APP_TOML"
cp --no-clobber "$APP_TOML" "$APP_TOML.bonynode-backup" || true
if grep -q '^minimum-gas-prices' "$APP_TOML"; then
sed -i 's|^minimum-gas-prices *=.*|minimum-gas-prices = "0.025uworrell"|' "$APP_TOML"
else
printf '\nminimum-gas-prices = "0.025uworrell"\n' >> "$APP_TOML"
fi
grep '^minimum-gas-prices' "$APP_TOML"
)Create the service, start and verify the node
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
BIN="worrelld"
CHAIN_ID="worrell-testnet-1"
NODE_HOME="$HOME/.worrell"
SERVICE="worrelld.service"
BIN_PATH=$(command -v "$BIN")
SERVICE_USER="$USER"
LOCAL_RPC_PORT="51657"
test -x "$BIN_PATH"
test -s "$NODE_HOME/config/genesis.json"
sudo tee "/etc/systemd/system/$SERVICE" >/dev/null <<EOF
[Unit]
Description=Worrell node
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=${SERVICE_USER}
ExecStart=${BIN_PATH} start --home ${NODE_HOME}
Restart=on-failure
RestartSec=5
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now "$SERVICE"
sudo systemctl status "$SERVICE" --no-pager
# Wait briefly for the local RPC listener and require the configured chain ID.
RPC_READY=0
for attempt in {1..30}; do
if STATUS=$(curl --fail --silent "http://127.0.0.1:$LOCAL_RPC_PORT/status"); then
jq -e --arg chain "$CHAIN_ID" '.result.node_info.network == $chain' <<<"$STATUS"
RPC_READY=1
break
fi
sleep 2
done
if [ "$RPC_READY" -ne 1 ]; then
echo 'Local RPC did not become ready; inspect the service journal.' >&2
sudo journalctl -u "$SERVICE" -n 100 --no-pager
false
fi
)Node sync checker
Compare the selected local RPC height with the chain-verified BonyNode reference. Stop with Ctrl+C.
#!/usr/bin/env bash
CHAIN_ID="worrell-testnet-1"
CONFIG="$HOME/.worrell/config/config.toml"
RPC_PORT=$(grep -m 1 -oP '^laddr = "\K[^"]+' "$CONFIG" | cut -d ':' -f 3)
RPC_PORT="${RPC_PORT:-51657}"
NETWORK_RPC="https://worrell-testnet-rpc.bonynode.online"
while true; do
LOCAL_STATUS=$(curl -fsS --max-time 10 "http://127.0.0.1:$RPC_PORT/status" 2>/dev/null || true)
NETWORK_STATUS=$(curl -fsS --max-time 10 "$NETWORK_RPC/status" 2>/dev/null || true)
LOCAL_CHAIN=$(jq -r '.result.node_info.network // empty' <<<"$LOCAL_STATUS" 2>/dev/null)
NETWORK_CHAIN=$(jq -r '.result.node_info.network // empty' <<<"$NETWORK_STATUS" 2>/dev/null)
LOCAL_HEIGHT=$(jq -r '.result.sync_info.latest_block_height // empty' <<<"$LOCAL_STATUS" 2>/dev/null)
NETWORK_HEIGHT=$(jq -r '.result.sync_info.latest_block_height // empty' <<<"$NETWORK_STATUS" 2>/dev/null)
if [[ "$LOCAL_CHAIN" != "$CHAIN_ID" || "$NETWORK_CHAIN" != "$CHAIN_ID" ||
! "$LOCAL_HEIGHT" =~ ^[0-9]+$ || ! "$NETWORK_HEIGHT" =~ ^[0-9]+$ ]]; then
echo -e "\033[1;31mError: invalid RPC response. Retrying...\033[0m"
sleep 5
continue
fi
BLOCKS_LEFT=$((NETWORK_HEIGHT - LOCAL_HEIGHT))
(( BLOCKS_LEFT < 0 )) && BLOCKS_LEFT=0
echo -e "\033[1;33mNode Height:\033[1;34m $LOCAL_HEIGHT\033[0m \033[1;33m| Network Height:\033[1;36m $NETWORK_HEIGHT\033[0m \033[1;33m| Blocks Left:\033[1;31m $BLOCKS_LEFT\033[0m"
sleep 5
doneCreate or restore wallet
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
BIN="worrelld"
WALLET='wallet'
[[ "$WALLET" =~ ^[a-zA-Z0-9_-]{1,64}$ ]] || { echo "Invalid wallet name" >&2; exit 1; }
# Create a new key. Store the mnemonic offline; it cannot be recovered by BonyNode.
"$BIN" keys add "$WALLET"
# To restore an existing key, run this separately:
# "$BIN" keys add "$WALLET" --recover
WALLET_ADDRESS=$("$BIN" keys show "$WALLET" -a)
"$BIN" query bank balances "$WALLET_ADDRESS"
)Create validator
Preferred by newer Cosmos SDK versions. The validator message is saved to a JSON file and shown before signing.
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
BIN="worrelld"
CHAIN_ID="worrell-testnet-1"
WALLET='wallet'
MONIKER='validator-name'
IDENTITY=''
WEBSITE=''
DETAILS='Independent validator'
AMOUNT="1000000uworrell"
MIN_SELF_DELEGATION="1"
COMMISSION_RATE="0.10"
COMMISSION_MAX_RATE="0.20"
COMMISSION_MAX_CHANGE="0.01"
VALIDATOR_FILE="$HOME/worrell-validator.json"
if PUBKEY_JSON=$("$BIN" comet show-validator 2>/dev/null); then
:
else
PUBKEY_JSON=$("$BIN" tendermint show-validator)
fi
jq -e 'type == "object"' <<<"$PUBKEY_JSON" >/dev/null
jq -n \
--argjson pubkey "$PUBKEY_JSON" \
--arg amount "$AMOUNT" \
--arg moniker "$MONIKER" \
--arg identity "$IDENTITY" \
--arg website "$WEBSITE" \
--arg details "$DETAILS" \
--arg rate "$COMMISSION_RATE" \
--arg maxRate "$COMMISSION_MAX_RATE" \
--arg maxChange "$COMMISSION_MAX_CHANGE" \
--arg minSelfDelegation "$MIN_SELF_DELEGATION" \
'{pubkey:$pubkey, amount:$amount, moniker:$moniker, identity:$identity,
website:$website, security:"", details:$details,
"commission-rate":$rate, "commission-max-rate":$maxRate,
"commission-max-change-rate":$maxChange,
"min-self-delegation":$minSelfDelegation}' > "$VALIDATOR_FILE"
jq -e 'type == "object" and .pubkey and .amount and .moniker' "$VALIDATOR_FILE" >/dev/null
cat "$VALIDATOR_FILE"
"$BIN" tx staking create-validator "$VALIDATOR_FILE" \
--from "$WALLET" \
--chain-id "$CHAIN_ID" \
--gas auto --gas-adjustment 1.5 --gas-prices 0.025uworrell \
--yes
)Basic host firewall
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
P2P_PORT="51656"
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow "$P2P_PORT/tcp"
sudo ufw enable
sudo ufw status verbose
# RPC/API/gRPC remain private unless you deliberately add authenticated
# reverse-proxy rules. Never expose validator signing services publicly.
)Advanced and recovery operations
Reversible decommission
This procedure stops and disables the service but retains the node home, binary and service file. Verify the backup before any later deletion.
(
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"
set -euo pipefail
SERVICE="worrelld"
NODE_HOME="$HOME/.worrell"
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
RETIRED_HOME="$NODE_HOME.retired.$STAMP"
KEY_BACKUP="$HOME/worrelld-validator-keys-$STAMP.tar.gz"
NODE_STOPPED=0
rollback() {
trap - ERR INT TERM
if [ "$NODE_STOPPED" -eq 1 ]; then sudo systemctl start "$SERVICE" || true; fi
exit 1
}
trap rollback ERR INT TERM
test -d "$NODE_HOME"
test ! -e "$RETIRED_HOME"
test -s "$NODE_HOME/config/priv_validator_key.json"
test -s "$NODE_HOME/data/priv_validator_state.json"
sudo systemctl stop "$SERVICE"
NODE_STOPPED=1
# Back up validator identity/state before moving anything.
tar -czf "$KEY_BACKUP" -C "$NODE_HOME" \
config/priv_validator_key.json config/node_key.json data/priv_validator_state.json
test -s "$KEY_BACKUP"
mv "$NODE_HOME" "$RETIRED_HOME"
sudo systemctl disable "$SERVICE"
NODE_STOPPED=0
trap - ERR INT TERM
echo "Node home was retained at: $RETIRED_HOME"
echo "Critical key backup: $KEY_BACKUP"
echo "No binary, service file or node data was deleted."
)