#!/bin/bash # C-Relay Build and Restart Script # Builds the project first, then stops any running relay and starts a new one in the background echo "=== C Nostr Relay Build and Restart Script ===" # Parse command line arguments PRESERVE_DATABASE=false HELP=false USE_TEST_KEYS=false ADMIN_KEY="" RELAY_KEY="" PORT_OVERRIDE="" DEBUG_LEVEL="5" # Key validation function validate_hex_key() { local key="$1" local key_type="$2" if [ ${#key} -ne 64 ]; then echo "ERROR: $key_type key must be exactly 64 characters" return 1 fi if ! [[ "$key" =~ ^[0-9a-fA-F]{64}$ ]]; then echo "ERROR: $key_type key must contain only hex characters (0-9, a-f, A-F)" return 1 fi return 0 } while [[ $# -gt 0 ]]; do case $1 in -a|--admin-key) if [ -z "$2" ]; then echo "ERROR: Admin key option requires a value" HELP=true shift else ADMIN_KEY="$2" shift 2 fi ;; -r|--relay-key) if [ -z "$2" ]; then echo "ERROR: Relay key option requires a value" HELP=true shift else RELAY_KEY="$2" shift 2 fi ;; -p|--port) if [ -z "$2" ]; then echo "ERROR: Port option requires a value" HELP=true shift else PORT_OVERRIDE="$2" shift 2 fi ;; -d|--preserve-database) PRESERVE_DATABASE=true shift ;; --test-keys|-t) USE_TEST_KEYS=true shift ;; --debug-level=*) DEBUG_LEVEL="${1#*=}" shift ;; -d=*) DEBUG_LEVEL="${1#*=}" shift ;; --debug-level) if [ -z "$2" ]; then echo "ERROR: Debug level option requires a value" HELP=true shift else DEBUG_LEVEL="$2" shift 2 fi ;; -d) if [ -z "$2" ]; then echo "ERROR: Debug level option requires a value" HELP=true shift else DEBUG_LEVEL="$2" shift 2 fi ;; --help|-h) HELP=true shift ;; *) echo "Unknown option: $1" HELP=true shift ;; esac done # Validate custom keys if provided if [ -n "$ADMIN_KEY" ]; then if ! validate_hex_key "$ADMIN_KEY" "Admin"; then exit 1 fi fi if [ -n "$RELAY_KEY" ]; then if ! validate_hex_key "$RELAY_KEY" "Relay"; then exit 1 fi fi # Validate port if provided if [ -n "$PORT_OVERRIDE" ]; then if ! [[ "$PORT_OVERRIDE" =~ ^[0-9]+$ ]] || [ "$PORT_OVERRIDE" -lt 1 ] || [ "$PORT_OVERRIDE" -gt 65535 ]; then echo "ERROR: Port must be a number between 1 and 65535" exit 1 fi fi # Validate strict port flag (only makes sense with port override) if [ "$USE_TEST_KEYS" = true ] && [ -z "$PORT_OVERRIDE" ]; then echo "WARNING: --strict-port is always used with test keys. Consider specifying a custom port with -p." fi # Validate debug level if provided if [ -n "$DEBUG_LEVEL" ]; then if ! [[ "$DEBUG_LEVEL" =~ ^[0-5]$ ]]; then echo "ERROR: Debug level must be 0-5, got: $DEBUG_LEVEL" exit 1 fi fi # Show help if [ "$HELP" = true ]; then echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " -a, --admin-key 64-character hex admin private key" echo " -r, --relay-key 64-character hex relay private key" echo " -p, --port Custom port override (default: 8888)" echo " -d, --debug-level <0-5> Set debug level: 0=none, 1=errors, 2=warnings, 3=info, 4=debug, 5=trace" echo " --preserve-database Keep existing database files (don't delete for fresh start)" echo " --test-keys, -t Use deterministic test keys for development (admin: all 'a's, relay: all '1's)" echo " --help, -h Show this help message" echo "" echo "Event-Based Configuration:" echo " This relay now uses event-based configuration stored directly in the database." echo " On first startup, keys are automatically generated and printed once." echo " Database file: .db (created automatically)" echo "" echo "Examples:" echo " $0 # Fresh start with random keys" echo " $0 -a -r # Use custom keys" echo " $0 -a -p 9000 # Custom admin key on port 9000" echo " $0 -p 7777 --strict-port # Fail if port 7777 unavailable (no fallback)" echo " $0 -p 8080 --strict-port -d=3 # Custom port with strict binding and debug" echo " $0 --debug-level=3 # Start with debug level 3 (info)" echo " $0 -d=5 # Start with debug level 5 (trace)" echo " $0 --preserve-database # Preserve existing database and keys" echo " $0 --test-keys # Use test keys for consistent development" echo " $0 -t --preserve-database # Use test keys and preserve database" echo "" echo "Key Format: Keys must be exactly 64 hexadecimal characters (0-9, a-f, A-F)" echo "Default behavior: Deletes existing database files to start fresh with new keys" echo " for development purposes" exit 0 fi # Handle database file cleanup for fresh start if [ "$PRESERVE_DATABASE" = false ]; then if ls *.db* >/dev/null 2>&1 || ls build/*.db* >/dev/null 2>&1; then echo "Removing existing database files (including WAL/SHM) to trigger fresh key generation..." rm -f *.db* build/*.db* echo "✓ Database files removed - will generate new keys and database" else echo "No existing database found - will generate fresh setup" fi else echo "Preserving existing database files (build process does not touch database files)" fi # Clean up legacy files that are no longer used rm -rf dev-config/ 2>/dev/null rm -f db/c_nostr_relay.db* 2>/dev/null # Embed web files into C headers before building echo "Embedding web files..." ./embed_web_files.sh # Build the project - ONLY static build echo "Building project (static binary with SQLite JSON1 extension)..." ./build_static.sh # Exit if static build fails - no fallback if [ $? -ne 0 ]; then echo "ERROR: Static build failed. Cannot proceed without static binary." echo "Please fix the build errors and try again." exit 1 fi # Check if build was successful if [ $? -ne 0 ]; then echo "ERROR: Build failed. Cannot restart relay." exit 1 fi # Check if static relay binary exists after build - ONLY use static binary ARCH=$(uname -m) case "$ARCH" in x86_64) BINARY_PATH="./build/c_relay_static_x86_64" ;; aarch64|arm64) BINARY_PATH="./build/c_relay_static_arm64" ;; *) BINARY_PATH="./build/c_relay_static_$ARCH" ;; esac # Verify static binary exists - no fallbacks if [ ! -f "$BINARY_PATH" ]; then echo "ERROR: Static relay binary not found: $BINARY_PATH" echo "" echo "The relay requires the static binary with JSON1 support." echo "Please run: ./build_static.sh" echo "" exit 1 fi echo "Using static binary: $BINARY_PATH" echo "Build successful. Proceeding with relay restart..." # Kill existing relay if running - start aggressive immediately echo "Stopping any existing relay servers..." # Get all relay processes and kill them immediately with -9 RELAY_PIDS=$(pgrep -f "c_relay_" || echo "") if [ -n "$RELAY_PIDS" ]; then echo "Force killing relay processes immediately: $RELAY_PIDS" kill -9 $RELAY_PIDS 2>/dev/null else echo "No existing relay processes found" fi # Ensure port 8888 is completely free with retry loop echo "Ensuring port 8888 is available..." for attempt in {1..15}; do if ! lsof -i :8888 >/dev/null 2>&1; then echo "Port 8888 is now free" break fi echo "Attempt $attempt: Port 8888 still in use, force killing..." # Kill anything using port 8888 fuser -k 8888/tcp 2>/dev/null || true # Double-check for any remaining relay processes REMAINING_PIDS=$(pgrep -f "c_relay_" || echo "") if [ -n "$REMAINING_PIDS" ]; then echo "Killing remaining relay processes: $REMAINING_PIDS" kill -9 $REMAINING_PIDS 2>/dev/null || true fi sleep 2 if [ $attempt -eq 15 ]; then echo "ERROR: Could not free port 8888 after 15 attempts" echo "Current processes using port:" lsof -i :8888 2>/dev/null || echo "No process details available" echo "You may need to manually kill processes or reboot" exit 1 fi done # Final safety check - ensure no relay processes remain FINAL_PIDS=$(pgrep -f "c_relay_" || echo "") if [ -n "$FINAL_PIDS" ]; then echo "Final cleanup: killing processes $FINAL_PIDS" kill -9 $FINAL_PIDS 2>/dev/null || true sleep 1 fi # Clean up PID file rm -f relay.pid # Database initialization is now handled automatically by the relay # with event-based configuration system echo "Database will be initialized automatically on startup if needed" # Start relay in background with output redirection echo "Starting relay server..." echo "Debug: Current processes: $(ps aux | grep 'c_relay_' | grep -v grep || echo 'None')" # Build command line arguments for relay binary RELAY_ARGS="" if [ -n "$ADMIN_KEY" ]; then RELAY_ARGS="$RELAY_ARGS -a $ADMIN_KEY" echo "Using custom admin key: ${ADMIN_KEY:0:16}..." fi if [ -n "$RELAY_KEY" ]; then RELAY_ARGS="$RELAY_ARGS -r $RELAY_KEY" echo "Using custom relay key: ${RELAY_KEY:0:16}..." fi if [ -n "$PORT_OVERRIDE" ]; then RELAY_ARGS="$RELAY_ARGS -p $PORT_OVERRIDE" echo "Using custom port: $PORT_OVERRIDE" fi if [ -n "$DEBUG_LEVEL" ]; then RELAY_ARGS="$RELAY_ARGS --debug-level=$DEBUG_LEVEL" echo "Using debug level: $DEBUG_LEVEL" fi # Change to build directory before starting relay so database files are created there cd build # Start relay in background and capture its PID if [ "$USE_TEST_KEYS" = true ]; then echo "Using deterministic test keys for development..." ./$(basename $BINARY_PATH) -a 6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3 -r 1111111111111111111111111111111111111111111111111111111111111111 --debug-level=$DEBUG_LEVEL --strict-port > ../relay.log 2>&1 & elif [ -n "$RELAY_ARGS" ]; then echo "Starting relay with custom configuration..." ./$(basename $BINARY_PATH) $RELAY_ARGS --debug-level=$DEBUG_LEVEL --strict-port > ../relay.log 2>&1 & else # No command line arguments needed for random key generation echo "Starting relay with random key generation..." ./$(basename $BINARY_PATH) --debug-level=$DEBUG_LEVEL --strict-port > ../relay.log 2>&1 & fi RELAY_PID=$! # Change back to original directory cd .. echo "Started with PID: $RELAY_PID" # Check if server is still running after short delay sleep 3 # Check if process is still alive if ps -p "$RELAY_PID" >/dev/null 2>&1; then echo "Relay started successfully!" echo "PID: $RELAY_PID" # Wait for relay to fully initialize and detect the actual port it's using sleep 2 # Extract actual port from relay logs ACTUAL_PORT="" if [ -f relay.log ]; then # Look for the success message with actual port ACTUAL_PORT=$(grep "WebSocket relay started on ws://127.0.0.1:" relay.log 2>/dev/null | tail -1 | sed -n 's/.*ws:\/\/127\.0\.0\.1:\([0-9]*\).*/\1/p') # If we couldn't find the port in logs, try to detect from netstat if [ -z "$ACTUAL_PORT" ]; then ACTUAL_PORT=$(netstat -tln 2>/dev/null | grep -E ":888[0-9]" | head -1 | sed -n 's/.*:\([0-9]*\).*/\1/p') fi fi # Display the actual endpoint if [ -n "$ACTUAL_PORT" ]; then if [ "$ACTUAL_PORT" = "8888" ]; then echo "WebSocket endpoint: ws://127.0.0.1:$ACTUAL_PORT" else echo "WebSocket endpoint: ws://127.0.0.1:$ACTUAL_PORT (fell back from port 8888)" fi else echo "WebSocket endpoint: ws://127.0.0.1:8888 (port detection failed - check logs)" fi echo "HTTP endpoint: http://127.0.0.1:${ACTUAL_PORT:-8888}" echo "Log file: relay.log" echo "" # Save PID for debugging echo $RELAY_PID > relay.pid # Check if new keys were generated and display them sleep 1 # Give relay time to write initial logs if grep -q "IMPORTANT: SAVE THIS ADMIN PRIVATE KEY SECURELY!" relay.log 2>/dev/null; then echo "=== IMPORTANT: NEW ADMIN PRIVATE KEY GENERATED ===" echo "" # Extract and display the admin private key section from the log grep -A 15 -B 2 "IMPORTANT: SAVE THIS ADMIN PRIVATE KEY SECURELY!" relay.log | head -n 20 echo "" echo "⚠️ SAVE THIS ADMIN PRIVATE KEY SECURELY - IT CONTROLS YOUR RELAY CONFIGURATION!" echo "⚠️ This key is needed to update configuration and is only displayed once" echo "⚠️ The relay and database information is also logged in relay.log for reference" echo "" fi echo "=== Event-Based Relay Server Running ===" echo "Configuration: Event-based (kind 33334 Nostr events)" echo "Database: Automatically created with relay pubkey naming" echo "To kill relay: pkill -f 'c_relay_'" echo "To check status: ps aux | grep c_relay_" echo "To view logs: tail -f relay.log" echo "Binary: $BINARY_PATH (zero configuration needed)" echo "Ready for Nostr client connections!" else echo "ERROR: Relay failed to start" echo "Debug: Check relay.log for error details:" echo "--- Last 10 lines of relay.log ---" tail -n 10 relay.log 2>/dev/null || echo "No log file found" echo "--- End log ---" exit 1 fi echo ""