#!/bin/sh # NodeMesh Agent Installer # Usage: curl -sSL https://install.nodemesh.app | sudo sh -s -- --token YOUR_TOKEN # or: sh install.sh --token YOUR_TOKEN # # This script: # 1. Detects the host OS/arch # 2. Downloads the nodemesh-agent binary # 3. Verifies the SHA-256 checksum (aborts on mismatch; graceful warning if manifest unavailable) # 4. Creates the config at /etc/nodemesh/agent.env # 5. Installs and starts a systemd service set -eu # ── root check ──────────────────────────────────────────────────────────────── # The script writes to /usr/local/bin and /etc/systemd/system — these require root. # Give the user an actionable re-run hint rather than a raw "Permission denied". if [ "$(id -u)" -ne 0 ]; then echo "Error: this script must be run as root." >&2 echo "" >&2 echo "Re-run with:" >&2 echo " curl -sSL https://install.nodemesh.app | sudo sh -s -- --token YOUR_TOKEN" >&2 exit 1 fi BACKEND_ADDR="${NODEMESH_BACKEND_ADDR:-api.nodemesh.app:443}" TLS_ENABLED="${NODEMESH_TLS_ENABLED:-true}" DOWNLOAD_BASE="${NODEMESH_DOWNLOAD_BASE:-https://nodemesh.app/releases}" INSTALL_DIR="/usr/local/bin" CONFIG_DIR="/etc/nodemesh" DATA_DIR="/opt/nodemesh/data/instances" SERVICE_FILE="/etc/systemd/system/nodemesh-agent.service" TOKEN="" # ── parse flags ─────────────────────────────────────────────────────────────── while [ $# -gt 0 ]; do case "$1" in --token) TOKEN="$2" shift 2 ;; --backend) BACKEND_ADDR="$2" shift 2 ;; --data-dir) DATA_DIR="$2" shift 2 ;; --no-tls) # For self-hosted backends without a TLS endpoint (local dev). TLS_ENABLED="false" shift ;; *) echo "Unknown flag: $1" >&2 exit 1 ;; esac done if [ -z "$TOKEN" ]; then printf "NodeMesh Agent Installer\n\nPaste your pairing token: " read -r TOKEN fi if [ -z "$TOKEN" ]; then echo "Error: pairing token is required." >&2 exit 1 fi # ── detect platform ─────────────────────────────────────────────────────────── OS="$(uname -s | tr '[:upper:]' '[:lower:]')" ARCH="$(uname -m)" case "$ARCH" in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64" ;; armv7l) ARCH="arm" ;; *) echo "Unsupported architecture: $ARCH" >&2 exit 1 ;; esac if [ "$OS" != "linux" ]; then echo "Only Linux is supported by the NodeMesh agent." >&2 exit 1 fi BINARY="nodemesh-agent-linux-${ARCH}" DOWNLOAD_URL="${DOWNLOAD_BASE}/${BINARY}" SUMS_URL="${DOWNLOAD_BASE}/../SHA256SUMS" # ── check for systemd ───────────────────────────────────────────────────────── if ! command -v systemctl >/dev/null 2>&1; then echo "Error: systemd is required to install the NodeMesh agent service." >&2 exit 1 fi # ── check for sha256sum ─────────────────────────────────────────────────────── # sha256sum is present on all mainstream Linux distros (coreutils). # We warn and continue rather than aborting if it's absent — keeps installs # working on minimal environments while strongly preferring verification. HAVE_SHA256SUM=false if command -v sha256sum >/dev/null 2>&1; then HAVE_SHA256SUM=true fi # ── download binary ─────────────────────────────────────────────────────────── echo "-> Downloading NodeMesh agent (${ARCH})..." TMP_BIN="$(mktemp)" if command -v curl >/dev/null 2>&1; then curl -sSL "$DOWNLOAD_URL" -o "$TMP_BIN" elif command -v wget >/dev/null 2>&1; then wget -qO "$TMP_BIN" "$DOWNLOAD_URL" else echo "Error: curl or wget is required." >&2 rm -f "$TMP_BIN" exit 1 fi # ── verify SHA-256 checksum ─────────────────────────────────────────────────── # Fetch the SHA256SUMS manifest and verify the downloaded binary. # If the manifest is unavailable (e.g. the deploy hasn't published it yet for # a new release), we print a WARNING and continue rather than breaking installs. # On hash mismatch we always abort — a corrupted binary is never installed. if [ "$HAVE_SHA256SUM" = "true" ]; then echo "-> Verifying SHA-256 checksum..." TMP_SUMS="$(mktemp)" SUMS_FETCHED=false if command -v curl >/dev/null 2>&1; then HTTP_STATUS="$(curl -sSL -o "$TMP_SUMS" -w "%{http_code}" "$SUMS_URL" 2>/dev/null || true)" elif command -v wget >/dev/null 2>&1; then HTTP_STATUS="$(wget -q -O "$TMP_SUMS" "$SUMS_URL" 2>/dev/null && echo "200" || echo "000")" fi if [ "${HTTP_STATUS:-000}" = "200" ] && [ -s "$TMP_SUMS" ]; then SUMS_FETCHED=true fi if [ "$SUMS_FETCHED" = "true" ]; then EXPECTED="$(grep "$BINARY" "$TMP_SUMS" 2>/dev/null | awk '{print $1}')" if [ -z "$EXPECTED" ]; then echo " WARNING: $BINARY not found in SHA256SUMS manifest." >&2 echo " Continuing without verification — manual verification recommended." >&2 else ACTUAL="$(sha256sum "$TMP_BIN" | awk '{print $1}')" if [ "$EXPECTED" = "$ACTUAL" ]; then echo " OK Checksum verified: $ACTUAL" else echo " ERROR: Checksum mismatch!" >&2 echo " Expected: $EXPECTED" >&2 echo " Got: $ACTUAL" >&2 echo " The downloaded binary has been deleted. Do not retry without investigating." >&2 rm -f "$TMP_BIN" "$TMP_SUMS" exit 1 fi fi else echo " WARNING: SHA256SUMS manifest unavailable (HTTP ${HTTP_STATUS:-???})." >&2 echo " Continuing without checksum verification." >&2 echo " You can verify manually: sha256sum ${INSTALL_DIR}/nodemesh-agent" >&2 echo " Published checksums: ${SUMS_URL}" >&2 fi rm -f "$TMP_SUMS" else echo " WARNING: sha256sum not found — skipping checksum verification." >&2 fi # ── install binary ───────────────────────────────────────────────────────────── chmod +x "$TMP_BIN" mv "$TMP_BIN" "${INSTALL_DIR}/nodemesh-agent" echo " OK Installed to ${INSTALL_DIR}/nodemesh-agent" # ── create config ───────────────────────────────────────────────────────────── echo "-> Writing config to ${CONFIG_DIR}/agent.env..." mkdir -p "$CONFIG_DIR" cat > "${CONFIG_DIR}/agent.env" < Installing systemd service..." cat > "$SERVICE_FILE" <