Files
nym/scripts/nym-node-setup/nym-node-install.sh
2025-11-13 20:56:04 +01:00

342 lines
11 KiB
Bash

#!/bin/bash
set -euo pipefail
echo -e "\n* * * Ensuring ~/nym-binaries exists * * *"
mkdir -p "$HOME/nym-binaries"
# load env.sh via absolute path if provided, else try ./env.sh
if [[ -n "${ENV_FILE:-}" && -f "${ENV_FILE}" ]]; then
set -a
# shellcheck disable=SC1090
. "${ENV_FILE}"
set +a
elif [[ -f "./env.sh" ]]; then
set -a
# shellcheck disable=SC1091
. ./env.sh
set +a
fi
# check for existing node config and optionally reset
NODE_CONFIG_DIR="$HOME/.nym/nym-nodes/default-nym-node"
check_existing_config() {
# proceed only if dir exists AND has any entries inside
if [[ -d "$NODE_CONFIG_DIR" ]] && find "$NODE_CONFIG_DIR" -mindepth 1 -maxdepth 1 | read -r _; then
echo
echo "Nym node configuration already exist at $NODE_CONFIG_DIR"
echo
echo "Initialising nym-node again will NOT overwrite your existing private keys, only adjust your preferences (like mode, wireguard optionality etc)."
echo
echo "If you want to remove your current node configuration and all data files including nodes keys type 'RESET' and press enter."
echo
read -r -p "To keep your existing node and just change its preferences press enter: " resp
if [[ "${resp}" =~ ^([Rr][Ee][Ss][Ee][Tt])$ ]]; then
echo
echo "We are going to remove the existing node with configuration $NODE_CONFIG_DIR and replace it with a fresh one."
read -r -p "back up the old one first? (Y/n) " backup_ans
if [[ -z "${backup_ans}" || "${backup_ans}" =~ ^[Yy]$ ]]; then
ts="$(date +%Y%m%d-%H%M%S)"
backup_dir="$HOME/.nym/backup/$(basename "$NODE_CONFIG_DIR")-$ts"
echo "Backing up to: $backup_dir"
mkdir -p "$(dirname "$backup_dir")"
cp -a "$NODE_CONFIG_DIR" "$backup_dir"
fi
echo "Removing $NODE_CONFIG_DIR ..."
rm -rf "$NODE_CONFIG_DIR"
echo "Old node removed. Proceeding with fresh initialization..."
else
echo "Keeping existing node configuration. Proceeding to re-configure."
export ASK_WG="1"
fi
fi
}
# run the check before any initialization
check_existing_config
# downloader for nym-node: probes platform-specific assets; if none exist,
# asks user for a direct downloadable URL and validates it.
download_nym_node() {
local latest_tag_url="$1" # e.g. "https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.13-emmental"
local dest_path="$2" # e.g. "$HOME/nym-binaries/nym-node"
local base_download_url
local os arch exe_ext=""
local candidates=()
local found_url=""
local http_code=""
if [[ -z "$latest_tag_url" || "$latest_tag_url" != *"/releases/tag/"* ]]; then
echo "ERROR: Invalid latest tag URL: $latest_tag_url" >&2
return 1
fi
base_download_url="${latest_tag_url/tag/download}"
# detect OS / ARCH
case "$(uname -s | tr '[:upper:]' '[:lower:]')" in
linux*) os="linux" ;;
darwin*) os="darwin" ;;
msys*|cygwin*|mingw*) os="windows"; exe_ext=".exe" ;;
*) echo "WARNING: Unknown OS; defaulting to linux." ; os="linux" ;;
esac
case "$(uname -m)" in
x86_64|amd64) arch="x86_64" ;;
aarch64|arm64) arch="aarch64" ;;
armv7*|armv6*|armv5*) arch="arm" ;;
*) arch="$(uname -m)";;
esac
# candidate asset names to probe (no assumptions about compression)
if [[ "$os" == "linux" ]]; then
candidates+=(
"nym-node"
"nym-node-${arch}-unknown-linux-gnu"
"nym-node-${arch}-unknown-linux-musl"
)
elif [[ "$os" == "darwin" ]]; then
candidates+=(
"nym-node-${arch}-apple-darwin"
"nym-node"
)
elif [[ "$os" == "windows" ]]; then
candidates+=(
"nym-node-${arch}-pc-windows-msvc${exe_ext}"
"nym-node${exe_ext}"
)
fi
# return 0 if URL exists (HTTP 200)
url_exists() {
local url="$1"
http_code="$(curl -sI -L -o /dev/null -w '%{http_code}' "$url")"
[[ "$http_code" == "200" ]]
}
echo -e "\n* * * Probing release assets for your platform ($os/$arch) * * *"
# try candidate assets in order
for name in "${candidates[@]}"; do
local try_url="${base_download_url}/${name}"
if url_exists "$try_url"; then
found_url="$try_url"
break
fi
done
# if nothing found, prompt the user for a URL
if [[ -z "$found_url" ]]; then
echo
echo "Could not find a 'nym-node' asset for your platform in the latest release:"
echo " $latest_tag_url"
echo
echo "HTTP check for first candidate (${base_download_url}/${candidates[0]}): ${http_code:-n/a}"
echo
echo "Please paste a direct, downloadable URL for the 'nym-node' binary for your platform."
echo "Tip: Open the GitHub release page, right-click the correct asset, and copy link address."
read -r -p "Custom download URL: " user_url
if [[ -z "${user_url// }" ]]; then
echo "ERROR: No URL provided. Aborting."
return 1
fi
if ! url_exists "$user_url"; then
echo "ERROR: The provided URL does not appear downloadable (HTTP $http_code). Aborting."
return 1
fi
found_url="$user_url"
fi
echo -e "\n* * * Downloading nym-node from: $found_url * * *"
mkdir -p "$(dirname "$dest_path")"
# remove any existing file to avoid 'text file busy'
if [[ -e "$dest_path" ]]; then
echo "Removing existing binary at $dest_path ..."
rm -f "$dest_path"
fi
if ! curl -fL "$found_url" -o "$dest_path"; then
echo "ERROR: Download failed from $found_url" >&2
return 1
fi
chmod +x "$dest_path" 2>/dev/null || true
echo "---------------------------------------------------"
echo "Nym node binary downloaded to: $dest_path"
"$dest_path" --version || true
echo "---------------------------------------------------"
}
echo -e "\n* * * Resolving latest release tag URL * * *"
LATEST_TAG_URL="$(curl -sI -L -o /dev/null -w '%{url_effective}' https://github.com/nymtech/nym/releases/latest)"
# expected example: https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.13-emmental
if [[ -z "${LATEST_TAG_URL}" || "${LATEST_TAG_URL}" != *"/releases/tag/"* ]]; then
echo "ERROR: Could not resolve latest tag URL from GitHub." >&2
exit 1
fi
NYM_NODE="$HOME/nym-binaries/nym-node"
# if binary already exists, ask to overwrite; if yes, remove first; if no, skip download
if [[ -e "${NYM_NODE}" ]]; then
echo
echo -e "\n* * * A nym-node binary already exists at: ${NYM_NODE}"
read -r -p "Overwrite with the latest release? (Y/n): " ow_ans
if [[ -z "${ow_ans}" || "${ow_ans}" =~ ^[Yy]$ ]]; then
echo "Removing existing binary..."
rm -f "${NYM_NODE}"
download_nym_node "$LATEST_TAG_URL" "$NYM_NODE"
else
echo "Keeping existing binary. Skipping download."
fi
else
# binary does not exist → must download
download_nym_node "$LATEST_TAG_URL" "$NYM_NODE"
fi
echo -e "\n * * * Making binary executable * * *"
chmod +x "${NYM_NODE}"
echo "---------------------------------------------------"
echo "Nym node binary downloaded:"
"${NYM_NODE}" --version || true
echo "---------------------------------------------------"
# check that MODE is set (after sourcing env.sh or other scripts)
if [[ -z "${MODE:-}" ]]; then
echo "ERROR: Environment variable MODE is not set."
echo "Please export MODE as one of: mixnode, entry-gateway, exit-gateway"
exit 1
fi
# determine public IP (fallback if ifconfig.me fails)
echo -e "\n* * * Discovering public IP (IPv4) * * *"
if ! PUBLIC_IP="$(curl -fsS -4 https://ifconfig.me)"; then
PUBLIC_IP="$(curl -fsS https://api.ipify.org || echo '')"
fi
if [[ -z "${PUBLIC_IP}" ]]; then
echo "WARNING: Could not determine public IP automatically."
fi
# respect existing WIREGUARD; for gateways: prompt if unset OR if we kept config and ASK_WG=1
WIREGUARD="${WIREGUARD:-}"
if [[ ( "$MODE" == "entry-gateway" || "$MODE" == "exit-gateway" ) && ( -n "${ASK_WG:-}" || -z "$WIREGUARD" ) ]]; then
echo
echo "Gateways can also route WireGuard in NymVPN."
def_hint=""
[[ -n "${WIREGUARD}" ]] && def_hint=" [current: ${WIREGUARD}]"
read -r -p "Enable WireGuard support? (Y/n)${def_hint}: " answer || true
if [[ -z "${answer}" || "${answer}" =~ ^[Yy]$ ]]; then
WIREGUARD="true"
elif [[ "${answer}" =~ ^[Nn]$ ]]; then
WIREGUARD="false"
fi
fi
# final default only if still empty
WIREGUARD="${WIREGUARD:-false}"
# persist WIREGUARD to the same env file Python CLI uses
ENV_PATH="${ENV_FILE:-./env.sh}"
if [[ -n "$ENV_PATH" ]]; then
mkdir -p "$(dirname "$ENV_PATH")"
if [[ -f "$ENV_PATH" ]]; then
# replace existing export or append
if grep -qE '^[[:space:]]*export[[:space:]]+WIREGUARD=' "$ENV_PATH"; then
sed -i -E 's|^[[:space:]]*export[[:space:]]+WIREGUARD=.*$|export WIREGUARD="'"$WIREGUARD"'"|' "$ENV_PATH"
else
printf '\nexport WIREGUARD="%s"\n' "$WIREGUARD" >> "$ENV_PATH"
fi
else
printf 'export WIREGUARD="%s"\n' "$WIREGUARD" > "$ENV_PATH"
fi
echo "WIREGUARD=${WIREGUARD} persisted to $ENV_PATH"
fi
# helpers: ensure optional env vars exist (avoid -u issues)
HOSTNAME="${HOSTNAME:-}"
LOCATION="${LOCATION:-}"
EMAIL="${EMAIL:-}"
MONIKER="${MONIKER:-}"
DESCRIPTION="${DESCRIPTION:-}"
# initialize node config
case "${MODE}" in
mixnode)
echo -e "\n* * * Initialising nym-node in mode: mixnode * * *"
"${NYM_NODE}" run \
--mode mixnode \
${PUBLIC_IP:+--public-ips "$PUBLIC_IP"} \
${HOSTNAME:+--hostname "$HOSTNAME"} \
${LOCATION:+--location "$LOCATION"} \
-w \
--init-only
;;
entry-gateway)
echo -e "\n* * * Initialising nym-node in mode: entry-gateway * * *"
"${NYM_NODE}" run \
--mode entry-gateway \
${PUBLIC_IP:+--public-ips "$PUBLIC_IP"} \
${HOSTNAME:+--hostname "$HOSTNAME"} \
${LOCATION:+--location "$LOCATION"} \
--wireguard-enabled "${WIREGUARD}" \
${HOSTNAME:+--landing-page-assets-path "/var/www/${HOSTNAME}"} \
-w \
--init-only
;;
exit-gateway)
echo -e "\n* * * Initialising nym-node in mode: exit-gateway * * *"
if [[ -z "${HOSTNAME:-}" || -z "${LOCATION:-}" ]]; then
echo "ERROR: HOSTNAME and LOCATION must be exported for exit-gateway."
exit 1
fi
"${NYM_NODE}" run \
--mode exit-gateway \
${PUBLIC_IP:+--public-ips "$PUBLIC_IP"} \
--hostname "$HOSTNAME" \
--location "$LOCATION" \
--wireguard-enabled "${WIREGUARD:-false}" \
--announce-wss-port 9001 \
--landing-page-assets-path "/var/www/${HOSTNAME}" \
-w \
--init-only
;;
*)
echo "ERROR: Unsupported MODE: '${MODE}'"
echo "Valid values: mixnode, entry-gateway, exit-gateway"
exit 1
;;
esac
echo
echo "* * * nym-node initialised. Config path should be:"
echo " $HOME/.nym/nym-nodes/default-nym-node/"
# setup description.toml (if init created the dir)
DESC_DIR="$HOME/.nym/nym-nodes/default-nym-node/data"
DESC_FILE="$DESC_DIR/description.toml"
if [[ -d "$DESC_DIR" ]]; then
echo -e "\n* * * Writing node description: $DESC_FILE * * *"
mkdir -p "$DESC_DIR"
cat > "$DESC_FILE" <<EOF
moniker = "${MONIKER}"
website = "${HOSTNAME}"
security_contact = "${EMAIL}"
details = "${DESCRIPTION}"
EOF
echo "* * * Node description saved * * *"
echo "You can edit it later at: $DESC_FILE (restart node to apply)."
else
echo "NOTE: Description directory not found yet ($DESC_DIR)."
echo " It will exist after a full init; you can create the file later."
fi