#!/bin/bash # DeBros Network Node Installation Script (Modern Node-Only Setup) # Installs, configures, and manages a DeBros network node with secure defaults. # Supports update-in-place, systemd service, and CLI management. set -e trap 'echo -e "${RED}An error occurred. Installation aborted.${NOCOLOR}"; exit 1' ERR # Color codes RED='\033[0;31m' GREEN='\033[0;32m' CYAN='\033[0;36m' BLUE='\033[38;2;2;128;175m' YELLOW='\033[1;33m' NOCOLOR='\033[0m' # Defaults INSTALL_DIR="/opt/debros" REPO_URL="https://github.com/DeBrosOfficial/network.git" MIN_GO_VERSION="1.21" NODE_PORT="4001" RQLITE_PORT="5001" GATEWAY_PORT="6001" RAFT_PORT="7001" UPDATE_MODE=false NON_INTERACTIVE=false log() { echo -e "${CYAN}[$(date '+%Y-%m-%d %H:%M:%S')]${NOCOLOR} $1"; } error() { echo -e "${RED}[ERROR]${NOCOLOR} $1"; } success() { echo -e "${GREEN}[SUCCESS]${NOCOLOR} $1"; } warning() { echo -e "${YELLOW}[WARNING]${NOCOLOR} $1"; } # Detect non-interactive mode if [ ! -t 0 ]; then NON_INTERACTIVE=true log "Running in non-interactive mode" fi # Root/sudo checks if [[ $EUID -eq 0 ]]; then warning "Running as root is not recommended for security reasons." if [ "$NON_INTERACTIVE" != true ]; then echo -n "Are you sure you want to continue? (yes/no): " read ROOT_CONFIRM if [[ "$ROOT_CONFIRM" != "yes" ]]; then error "Installation cancelled for security reasons." exit 1 fi else log "Non-interactive mode: proceeding with root (use at your own risk)" fi alias sudo='' else if ! command -v sudo &>/dev/null; then error "sudo command not found. Please ensure you have sudo privileges." exit 1 fi fi # Detect OS and package manager detect_os() { if [ -f /etc/os-release ]; then . /etc/os-release OS=$ID VERSION=$VERSION_ID else error "Cannot detect operating system" exit 1 fi case $OS in ubuntu|debian) PACKAGE_MANAGER="apt" ;; centos|rhel|fedora) PACKAGE_MANAGER="yum" if command -v dnf &> /dev/null; then PACKAGE_MANAGER="dnf"; fi ;; *) error "Unsupported operating system: $OS"; exit 1 ;; esac log "Detected OS: $OS $VERSION" } # Check for existing install check_existing_installation() { if [ -d "$INSTALL_DIR" ] && [ -f "$INSTALL_DIR/bin/node" ]; then log "Found existing DeBros Network installation at $INSTALL_DIR" NODE_RUNNING=false if systemctl is-active --quiet debros-node.service 2>/dev/null; then NODE_RUNNING=true log "Node service is currently running" fi if [ "$NON_INTERACTIVE" = true ]; then log "Non-interactive mode: updating existing installation" UPDATE_MODE=true return 0 fi echo -e "${YELLOW}Existing installation detected!${NOCOLOR}" echo -e "${CYAN}Options:${NOCOLOR}" echo -e "${CYAN}1) Update existing installation${NOCOLOR}" echo -e "${CYAN}2) Remove and reinstall${NOCOLOR}" echo -e "${CYAN}3) Exit installer${NOCOLOR}" while true; do read -rp "Enter your choice (1, 2, or 3): " EXISTING_CHOICE case $EXISTING_CHOICE in 1) UPDATE_MODE=true; log "Will update existing installation"; return 0 ;; 2) log "Will remove and reinstall"; remove_existing_installation; UPDATE_MODE=false; return 0 ;; 3) log "Installation cancelled by user"; exit 0 ;; *) error "Invalid choice. Please enter 1, 2, or 3." ;; esac done else UPDATE_MODE=false return 0 fi } remove_existing_installation() { log "Removing existing installation..." for service in debros-node; do if systemctl list-unit-files | grep -q "$service.service"; then log "Stopping $service service..." sudo systemctl stop $service.service 2>/dev/null || true sudo systemctl disable $service.service 2>/dev/null || true sudo rm -f /etc/systemd/system/$service.service fi done sudo systemctl daemon-reload if [ -d "$INSTALL_DIR" ]; then sudo rm -rf "$INSTALL_DIR" log "Removed installation directory" fi if id "debros" &>/dev/null; then sudo userdel debros 2>/dev/null || true log "Removed debros user" fi success "Existing installation removed" } check_go_installation() { if command -v go &> /dev/null; then GO_VERSION=$(go version | awk '{print $3}' | sed 's/go//') log "Found Go version: $GO_VERSION" if [ "$(printf '%s\n' "$MIN_GO_VERSION" "$GO_VERSION" | sort -V | head -n1)" = "$MIN_GO_VERSION" ]; then success "Go version is sufficient" return 0 else warning "Go version $GO_VERSION is too old. Minimum required: $MIN_GO_VERSION" return 1 fi else log "Go not found on system" return 1 fi } install_go() { log "Installing Go..." case $PACKAGE_MANAGER in apt) sudo apt update; sudo apt install -y wget ;; yum|dnf) sudo $PACKAGE_MANAGER install -y wget ;; esac GO_TARBALL="go1.21.6.linux-amd64.tar.gz" ARCH=$(uname -m) if [ "$ARCH" = "aarch64" ]; then GO_TARBALL="go1.21.6.linux-arm64.tar.gz"; fi cd /tmp wget -q "https://go.dev/dl/$GO_TARBALL" sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "$GO_TARBALL" if ! grep -q "/usr/local/go/bin" /etc/environment 2>/dev/null; then echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/go/bin"' | sudo tee /etc/environment > /dev/null fi if ! grep -q "/usr/local/go/bin" ~/.bashrc; then echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc fi export PATH=$PATH:/usr/local/go/bin success "Go installed successfully" } install_dependencies() { log "Checking system dependencies..." MISSING_DEPS=() case $PACKAGE_MANAGER in apt) for pkg in git make build-essential curl; do if ! dpkg -l | grep -q "^ii $pkg "; then MISSING_DEPS+=($pkg); fi done if [ ${#MISSING_DEPS[@]} -gt 0 ]; then log "Installing missing dependencies: ${MISSING_DEPS[*]}" sudo apt update sudo apt install -y "${MISSING_DEPS[@]}" else success "All system dependencies already installed" fi ;; yum|dnf) for pkg in git make curl; do if ! rpm -q $pkg &>/dev/null; then MISSING_DEPS+=($pkg); fi done if ! rpm -q gcc &>/dev/null; then MISSING_DEPS+=("Development Tools"); fi if [ ${#MISSING_DEPS[@]} -gt 0 ]; then log "Installing missing dependencies: ${MISSING_DEPS[*]}" if [[ " ${MISSING_DEPS[*]} " =~ " Development Tools " ]]; then sudo $PACKAGE_MANAGER groupinstall -y "Development Tools" fi MISSING_DEPS=($(printf '%s\n' "${MISSING_DEPS[@]}" | grep -v "Development Tools")) if [ ${#MISSING_DEPS[@]} -gt 0 ]; then sudo $PACKAGE_MANAGER install -y "${MISSING_DEPS[@]}" fi else success "All system dependencies already installed" fi ;; esac success "System dependencies ready" } install_rqlite() { if command -v rqlited &> /dev/null; then RQLITE_VERSION=$(rqlited -version | head -n1 | awk '{print $2}') log "Found RQLite version: $RQLITE_VERSION" success "RQLite already installed" return 0 fi log "Installing RQLite..." ARCH=$(uname -m) case $ARCH in x86_64) RQLITE_ARCH="amd64" ;; aarch64|arm64) RQLITE_ARCH="arm64" ;; armv7l) RQLITE_ARCH="arm" ;; *) error "Unsupported architecture: $ARCH"; exit 1 ;; esac RQLITE_VERSION="8.43.0" RQLITE_TARBALL="rqlite-v${RQLITE_VERSION}-linux-${RQLITE_ARCH}.tar.gz" RQLITE_URL="https://github.com/rqlite/rqlite/releases/download/v${RQLITE_VERSION}/${RQLITE_TARBALL}" cd /tmp if ! wget -q "$RQLITE_URL"; then error "Failed to download RQLite from $RQLITE_URL"; exit 1; fi tar -xzf "$RQLITE_TARBALL" RQLITE_DIR="rqlite-v${RQLITE_VERSION}-linux-${RQLITE_ARCH}" sudo cp "$RQLITE_DIR/rqlited" /usr/local/bin/ sudo cp "$RQLITE_DIR/rqlite" /usr/local/bin/ sudo chmod +x /usr/local/bin/rqlited sudo chmod +x /usr/local/bin/rqlite rm -rf "$RQLITE_TARBALL" "$RQLITE_DIR" if command -v rqlited &> /dev/null; then INSTALLED_VERSION=$(rqlited -version | head -n1 | awk '{print $2}') success "RQLite v$INSTALLED_VERSION installed successfully" else error "RQLite installation failed" exit 1 fi } check_ports() { local ports=($NODE_PORT $RQLITE_PORT $RAFT_PORT $GATEWAY_PORT) for port in "${ports[@]}"; do if sudo netstat -tuln 2>/dev/null | grep -q ":$port " || ss -tuln 2>/dev/null | grep -q ":$port "; then error "Port $port is already in use. Please free it up and try again." exit 1 fi done success "All required ports are available" } configuration_wizard() { log "${BLUE}==================================================${NOCOLOR}" log "${GREEN} DeBros Network Configuration Wizard ${NOCOLOR}" log "${BLUE}==================================================${NOCOLOR}" if [ "$NON_INTERACTIVE" = true ]; then log "Non-interactive mode: using default configuration" SOLANA_WALLET="11111111111111111111111111111111" CONFIGURE_FIREWALL="yes" log "Installation Directory: $INSTALL_DIR" log "Firewall Configuration: $CONFIGURE_FIREWALL" success "Configuration completed with defaults" return 0 fi log "${GREEN}Enter your Solana wallet address for node operator rewards:${NOCOLOR}" while true; do read -rp "Solana Wallet Address: " SOLANA_WALLET if [[ -n "$SOLANA_WALLET" && ${#SOLANA_WALLET} -ge 32 ]]; then break; else error "Please enter a valid Solana wallet address"; fi done read -rp "Installation directory [default: $INSTALL_DIR]: " CUSTOM_INSTALL_DIR if [[ -n "$CUSTOM_INSTALL_DIR" ]]; then INSTALL_DIR="$CUSTOM_INSTALL_DIR"; fi read -rp "Configure firewall automatically? (yes/no) [default: yes]: " CONFIGURE_FIREWALL CONFIGURE_FIREWALL="${CONFIGURE_FIREWALL:-yes}" success "Configuration completed" } setup_directories() { log "Setting up directories and permissions..." if ! id "debros" &>/dev/null; then sudo useradd -r -s /bin/false -d "$INSTALL_DIR" debros log "Created debros user" else log "User 'debros' already exists" fi sudo mkdir -p "$INSTALL_DIR"/{bin,configs,keys,data,logs,src} sudo mkdir -p "$INSTALL_DIR/keys/node" sudo mkdir -p "$INSTALL_DIR/data/node"/{rqlite,storage} sudo chown -R debros:debros "$INSTALL_DIR" sudo chmod 755 "$INSTALL_DIR" sudo chmod 700 "$INSTALL_DIR/keys" sudo chmod 700 "$INSTALL_DIR/keys/node" sudo chmod 755 "$INSTALL_DIR/data" sudo chmod 755 "$INSTALL_DIR/logs" sudo chmod 755 "$INSTALL_DIR/configs" sudo chmod 755 "$INSTALL_DIR/bin" success "Directory structure ready" } setup_source_code() { log "Setting up source code..." if [ -d "$INSTALL_DIR/src/.git" ]; then log "Updating existing repository..." cd "$INSTALL_DIR/src" sudo -u debros git pull else log "Cloning repository..." sudo -u debros git clone "$REPO_URL" "$INSTALL_DIR/src" cd "$INSTALL_DIR/src" fi success "Source code ready" } generate_identity() { local identity_file="$INSTALL_DIR/keys/node/identity.key" if [ -f "$identity_file" ]; then if [ "$UPDATE_MODE" = true ]; then log "Identity key already exists, keeping existing key" success "Using existing node identity" return 0 else log "Identity key already exists, regenerating..." sudo rm -f "$identity_file" fi fi log "Generating node identity..." cd "$INSTALL_DIR/src" cat > /tmp/generate_identity_custom.go << 'EOF' package main import ( "crypto/rand" "flag" "fmt" "os" "path/filepath" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" ) func main() { var outputPath string flag.StringVar(&outputPath, "output", "", "Output path for identity key") flag.Parse() if outputPath == "" { fmt.Println("Usage: go run generate_identity_custom.go -output ") os.Exit(1) } priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, rand.Reader) if err != nil { panic(err) } peerID, err := peer.IDFromPublicKey(pub) if err != nil { panic(err) } data, err := crypto.MarshalPrivateKey(priv) if err != nil { panic(err) } if err := os.MkdirAll(filepath.Dir(outputPath), 0700); err != nil { panic(err) } if err := os.WriteFile(outputPath, data, 0600); err != nil { panic(err) } fmt.Printf("Generated Peer ID: %s\n", peerID.String()) fmt.Printf("Identity saved to: %s\n", outputPath) } EOF export PATH=$PATH:/usr/local/go/bin sudo -u debros env "PATH=$PATH:/usr/local/go/bin" "GOMOD=$(pwd)" go run /tmp/generate_identity_custom.go -output "$identity_file" rm /tmp/generate_identity_custom.go success "Node identity generated" } build_binaries() { log "Building DeBros Network binaries..." cd "$INSTALL_DIR/src" export PATH=$PATH:/usr/local/go/bin sudo -u debros env "PATH=$PATH:/usr/local/go/bin" make build local services_were_running=() if [ "$UPDATE_MODE" = true ]; then log "Update mode: checking for running services before binary update..." if systemctl is-active --quiet debros-node.service 2>/dev/null; then log "Stopping debros-node service to update binaries..." sudo systemctl stop debros-node.service services_were_running+=("debros-node") fi if [ ${#services_were_running[@]} -gt 0 ]; then log "Waiting for services to stop completely..." sleep 3 fi fi sudo cp bin/* "$INSTALL_DIR/bin/" sudo chown debros:debros "$INSTALL_DIR/bin/"* if [ "$UPDATE_MODE" = true ] && [ ${#services_were_running[@]} -gt 0 ]; then log "Restarting previously running services..." for service in "${services_were_running[@]}"; do log "Starting $service service..." sudo systemctl start $service.service done fi success "Binaries built and installed" } generate_configs() { log "Generating configuration files..." cat > /tmp/node.yaml << EOF node: data_dir: "$INSTALL_DIR/data/node" key_file: "$INSTALL_DIR/keys/node/identity.key" listen_addresses: - "/ip4/0.0.0.0/tcp/$NODE_PORT" solana_wallet: "$SOLANA_WALLET" database: rqlite_port: $RQLITE_PORT rqlite_raft_port: $RAFT_PORT logging: level: "info" file: "$INSTALL_DIR/logs/node.log" EOF sudo mv /tmp/node.yaml "$INSTALL_DIR/configs/node.yaml" sudo chown debros:debros "$INSTALL_DIR/configs/node.yaml" success "Configuration files generated" } configure_firewall() { if [[ "$CONFIGURE_FIREWALL" == "yes" ]]; then log "Configuring firewall rules..." if command -v ufw &> /dev/null; then log "Adding UFW rules for DeBros Network ports..." for port in $NODE_PORT $RQLITE_PORT $RAFT_PORT $GATEWAY_PORT; do if ! sudo ufw allow $port; then error "Failed to allow port $port" exit 1 fi log "Added UFW rule: allow port $port" done UFW_STATUS=$(sudo ufw status | grep -o "Status: [a-z]\+" | awk '{print $2}' || echo "inactive") if [[ "$UFW_STATUS" == "active" ]]; then success "Firewall rules added and active" else success "Firewall rules added (UFW is inactive - rules will take effect when UFW is enabled)" log "To enable UFW with current rules: sudo ufw enable" fi else warning "UFW not found. Please configure firewall manually." log "Required ports to allow:" log " - Port $NODE_PORT (Node)" log " - Port $RQLITE_PORT (RQLite)" log " - Port $RAFT_PORT (Raft)" fi fi } create_systemd_service() { local service_file="/etc/systemd/system/debros-node.service" if [ -f "$service_file" ]; then log "Cleaning up existing node service..." sudo systemctl stop debros-node.service 2>/dev/null || true sudo systemctl disable debros-node.service 2>/dev/null || true sudo rm -f "$service_file" fi sudo systemctl daemon-reload log "Creating new systemd service..." local exec_start="$INSTALL_DIR/bin/node -data $INSTALL_DIR/data/node" cat > /tmp/debros-node.service << EOF [Unit] Description=DeBros Network Node After=network.target Wants=network-online.target [Service] Type=simple User=debros Group=debros WorkingDirectory=$INSTALL_DIR Environment=ENVIRONMENT=production ExecStart=$exec_start Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=debros-node NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=$INSTALL_DIR [Install] WantedBy=multi-user.target EOF sudo mv /tmp/debros-node.service "$service_file" sudo systemctl daemon-reload sudo systemctl enable debros-node.service success "Systemd service ready" } start_service() { log "Starting DeBros Network service..." sudo systemctl start debros-node.service sleep 3 if systemctl is-active --quiet debros-node.service; then success "DeBros Network service started successfully" else error "Failed to start DeBros Network service" log "Check logs with: sudo journalctl -u debros-node.service" exit 1 fi } display_banner() { echo -e "${BLUE}========================================================================${NOCOLOR}" echo -e "${CYAN} ____ ____ _ _ _ _ | _ \\ ___| __ ) _ __ ___ ___ | \\ | | ___| |___ _____ _ __| | __ | | | |/ _ \\ _ \\| __/ _ \\/ __| | \\| |/ _ \\ __\\ \\ /\\ / / _ \\| __| |/ / | |_| | __/ |_) | | | (_) \\__ \\ | |\\ | __/ |_ \\ V V / (_) | | | < |____/ \\___|____/|_| \\___/|___/ |_| \\_|\\___|\\__| \\_/\\_/ \\___/|_| |_|\\_\\ ${NOCOLOR}" echo -e "${BLUE}========================================================================${NOCOLOR}" } main() { display_banner log "${BLUE}==================================================${NOCOLOR}" log "${GREEN} Starting DeBros Network Installation ${NOCOLOR}" log "${BLUE}==================================================${NOCOLOR}" detect_os check_existing_installation if [ "$UPDATE_MODE" != true ]; then check_ports; else log "Update mode: skipping port availability check"; fi if ! check_go_installation; then install_go; fi install_dependencies install_rqlite if [ "$UPDATE_MODE" != true ]; then configuration_wizard; else log "Update mode: skipping configuration wizard" SOLANA_WALLET="11111111111111111111111111111111" CONFIGURE_FIREWALL="yes" fi setup_directories setup_source_code generate_identity build_binaries if [ "$UPDATE_MODE" != true ]; then generate_configs configure_firewall else log "Update mode: keeping existing configuration" fi create_systemd_service start_service log "${BLUE}==================================================${NOCOLOR}" if [ "$UPDATE_MODE" = true ]; then log "${GREEN} Update Complete! ${NOCOLOR}" else log "${GREEN} Installation Complete! ${NOCOLOR}" fi log "${BLUE}==================================================${NOCOLOR}" log "${GREEN}Installation Directory:${NOCOLOR} ${CYAN}$INSTALL_DIR${NOCOLOR}" log "${GREEN}Configuration:${NOCOLOR} ${CYAN}$INSTALL_DIR/configs/node.yaml${NOCOLOR}" log "${GREEN}Logs:${NOCOLOR} ${CYAN}$INSTALL_DIR/logs/node.log${NOCOLOR}" log "${GREEN}Node Port:${NOCOLOR} ${CYAN}$NODE_PORT${NOCOLOR}" log "${GREEN}RQLite Port:${NOCOLOR} ${CYAN}$RQLITE_PORT${NOCOLOR}" log "${GREEN}Raft Port:${NOCOLOR} ${CYAN}$RAFT_PORT${NOCOLOR}" log "${GREEN}Gateway Port:${NOCOLOR} ${CYAN}$GATEWAY_PORT${NOCOLOR}" log "${BLUE}==================================================${NOCOLOR}" log "${GREEN}Management Commands:${NOCOLOR}" log "${CYAN} - sudo systemctl status debros-node${NOCOLOR} (Check status)" log "${CYAN} - sudo systemctl restart debros-node${NOCOLOR} (Restart service)" log "${CYAN} - sudo systemctl stop debros-node${NOCOLOR} (Stop service)" log "${CYAN} - sudo systemctl start debros-node${NOCOLOR} (Start service)" log "${CYAN} - sudo journalctl -u debros-node.service -f${NOCOLOR} (View logs)" log "${CYAN} - $INSTALL_DIR/bin/network-cli${NOCOLOR} (Use CLI tools)" log "${BLUE}==================================================${NOCOLOR}" if [ "$UPDATE_MODE" = true ]; then success "DeBros Network has been updated and is running!" else success "DeBros Network is now running!" fi log "${CYAN}For documentation visit: https://docs.debros.io${NOCOLOR}" } main "$@"