#!/bin/bash # Ginxsom WebSocket Admin Test Script # Tests Kind 23456/23457 admin command system over WebSocket with NIP-44 encryption # # Prerequisites: # - websocat: WebSocket client (https://github.com/vi/websocat) # - nak: Nostr Army Knife (https://github.com/fiatjaf/nak) # - jq: JSON processor # - Server running with test keys from .test_keys set -e # Configuration WEBSOCKET_URL="wss://localhost:9443/admin" # Secure WebSocket via nginx HTTPS WEBSOCKET_HTTP_URL="ws://localhost:9001/admin" # Non-secure WebSocket via nginx HTTP WEBSOCKET_DIRECT_URL="ws://localhost:9442" # Direct connection to WebSocket server (port 9442) TEST_KEYS_FILE=".test_keys" TIMEOUT=10 # WebSocket connection timeout in seconds # Load test keys if [[ ! -f "$TEST_KEYS_FILE" ]]; then echo "ERROR: $TEST_KEYS_FILE not found" echo "Run the server with --test-keys to generate test keys" exit 1 fi source "$TEST_KEYS_FILE" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Helper functions log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } log_debug() { echo -e "${CYAN}[DEBUG]${NC} $1" } check_dependencies() { log_info "Checking dependencies..." for cmd in websocat nak jq; do if ! command -v $cmd &> /dev/null; then log_error "$cmd is not installed" case $cmd in websocat) echo "Install from: https://github.com/vi/websocat" echo " cargo install websocat" ;; nak) echo "Install from: https://github.com/fiatjaf/nak" echo " go install github.com/fiatjaf/nak@latest" ;; jq) echo "Install jq for JSON processing" echo " apt-get install jq # Debian/Ubuntu" ;; esac exit 1 fi done log_success "All dependencies found" log_info " websocat: $(websocat --version 2>&1 | head -n1)" log_info " nak: $(nak --version 2>&1 | head -n1)" log_info " jq: $(jq --version 2>&1)" } # Test basic WebSocket connection test_websocket_connection() { local url="$1" log_info "=== Testing WebSocket Connection ===" log_info "Connecting to: $url" # For wss:// connections, add --insecure flag to skip certificate verification local websocat_opts="" if [[ "$url" == wss://* ]]; then websocat_opts="--insecure" log_debug "Using --insecure flag for self-signed certificate" fi # Try to connect and send a ping local result=$(timeout $TIMEOUT websocat $websocat_opts -n1 "$url" <<< '{"test":"ping"}' 2>&1 || echo "TIMEOUT") if [[ "$result" == "TIMEOUT" ]]; then log_error "Connection timeout after ${TIMEOUT}s" return 1 elif [[ -z "$result" ]]; then log_warning "Connected but no response (this may be normal for WebSocket)" return 0 else log_success "Connection established" log_debug "Response: $result" return 0 fi } # Create NIP-44 encrypted admin command event (Kind 23456) create_admin_command_event() { local command="$1" local expiration=$(($(date +%s) + 3600)) # 1 hour from now log_info "Creating Kind 23456 admin command event..." log_info "Command: $command" # Content is a JSON array of commands local content="[\"$command\"]" # Create event with nak # Kind 23456 = admin command # Tags: p = server pubkey, expiration local event=$(nak event -k 23456 \ -c "$content" \ --tag p="$SERVER_PUBKEY" \ --tag expiration="$expiration" \ --sec "$ADMIN_PRIVKEY" 2>&1) if [[ $? -ne 0 ]]; then log_error "Failed to create event with nak" log_error "$event" return 1 fi echo "$event" } # Send admin command via WebSocket and wait for response send_websocket_admin_command() { local command="$1" local url="$2" log_info "=== Testing Admin Command via WebSocket: $command ===" # Create Kind 23456 event local event=$(create_admin_command_event "$command") if [[ -z "$event" ]]; then log_error "Failed to create admin event" return 1 fi log_success "Event created successfully" log_debug "Event JSON:" echo "$event" | jq -C . 2>/dev/null || echo "$event" echo "" # Send to WebSocket server and wait for response log_info "Sending to WebSocket: $url" log_info "Waiting for Kind 23457 response (timeout: ${TIMEOUT}s)..." # For wss:// connections, add --insecure flag to skip certificate verification local websocat_opts="" if [[ "$url" == wss://* ]]; then websocat_opts="--insecure" log_debug "Using --insecure flag for self-signed certificate" fi # Use websocat to send event and receive response local response=$(timeout $TIMEOUT websocat $websocat_opts -n1 "$url" <<< "$event" 2>&1) local exit_code=$? echo "" if [[ $exit_code -eq 124 ]]; then log_error "Timeout waiting for response after ${TIMEOUT}s" return 1 elif [[ $exit_code -ne 0 ]]; then log_error "WebSocket connection failed (exit code: $exit_code)" log_error "$response" return 1 fi if [[ -z "$response" ]]; then log_warning "No response received (connection may have closed)" return 1 fi log_success "Response received" log_debug "Raw response:" echo "$response" echo "" # Try to parse as JSON if echo "$response" | jq . &>/dev/null; then log_success "Valid JSON response" # Check if it's a Kind 23457 event local kind=$(echo "$response" | jq -r '.kind // empty' 2>/dev/null) if [[ "$kind" == "23457" ]]; then log_success "Received Kind 23457 response event ✓" # Extract and display response details local response_id=$(echo "$response" | jq -r '.id // empty') local response_pubkey=$(echo "$response" | jq -r '.pubkey // empty') local response_content=$(echo "$response" | jq -r '.content // empty') local response_sig=$(echo "$response" | jq -r '.sig // empty') echo "" log_info "Response Event Details:" log_info " ID: $response_id" log_info " Pubkey: $response_pubkey" log_info " Content: $response_content" log_info " Sig: ${response_sig:0:32}..." # Check if content is encrypted (NIP-44) if [[ ${#response_content} -gt 50 ]]; then log_info " Content appears to be NIP-44 encrypted" log_warning " Decryption not yet implemented in test script" else log_info " Content (plaintext): $response_content" fi # Verify signature log_info "Verifying event signature..." if echo "$response" | nak verify 2>&1 | grep -q "signature is valid"; then log_success "Event signature is valid ✓" else log_error "Event signature verification failed" return 1 fi else log_warning "Response is not Kind 23457 (got kind: $kind)" fi # Pretty print the full response echo "" log_info "Full Response Event:" echo "$response" | jq -C . else log_warning "Response is not valid JSON" log_debug "Raw response: $response" fi echo "" return 0 } # Test config_query command test_config_query() { log_info "=== Testing config_query Command ===" send_websocket_admin_command "config_query" "$WEBSOCKET_URL" } # Test with HTTP WebSocket connection test_http_connection() { log_info "=== Testing HTTP WebSocket Connection ===" log_info "Connecting via HTTP (port 9001)" send_websocket_admin_command "config_query" "$WEBSOCKET_HTTP_URL" } # Test with direct WebSocket connection (bypassing nginx) test_direct_connection() { log_info "=== Testing Direct WebSocket Connection ===" log_info "Connecting directly to WebSocket server (port 9442)" send_websocket_admin_command "config_query" "$WEBSOCKET_DIRECT_URL" } # Test invalid command test_invalid_command() { log_info "=== Testing Invalid Command ===" send_websocket_admin_command "invalid_command_xyz" "$WEBSOCKET_URL" || log_warning "Expected failure for invalid command" } # Test connection persistence test_connection_persistence() { log_info "=== Testing Connection Persistence ===" log_info "Sending multiple commands over same connection..." # Create two events local event1=$(create_admin_command_event "config_query") local event2=$(create_admin_command_event "config_query") if [[ -z "$event1" ]] || [[ -z "$event2" ]]; then log_error "Failed to create events" return 1 fi # For wss:// connections, add --insecure flag local websocat_opts="" if [[ "$WEBSOCKET_URL" == wss://* ]]; then websocat_opts="--insecure" fi # Send both events and collect responses log_info "Sending two events sequentially..." local responses=$(timeout $((TIMEOUT * 2)) websocat $websocat_opts -n2 "$WEBSOCKET_URL" </dev/null || echo "$line" fi done else log_warning "Connection persistence test inconclusive" fi echo "" } main() { echo "==========================================" echo " Ginxsom WebSocket Admin Test Suite" echo " Kind 23456/23457 over WebSocket" echo "==========================================" echo "" log_info "Test Configuration:" log_info " Admin Privkey: ${ADMIN_PRIVKEY:0:16}...${ADMIN_PRIVKEY: -16}" log_info " Admin Pubkey: $ADMIN_PUBKEY" log_info " Server Pubkey: $SERVER_PUBKEY" log_info " HTTPS URL: $WEBSOCKET_URL" log_info " HTTP URL: $WEBSOCKET_HTTP_URL" log_info " Direct URL: $WEBSOCKET_DIRECT_URL" log_info " Timeout: ${TIMEOUT}s" echo "" check_dependencies echo "" # Test basic WebSocket connectivity if ! test_websocket_connection "$WEBSOCKET_URL"; then log_error "Basic WebSocket connection failed" log_info "Trying direct connection to port 9442..." if ! test_websocket_connection "$WEBSOCKET_DIRECT_URL"; then log_error "Direct connection also failed" log_error "Make sure the server is running with WebSocket admin enabled" exit 1 fi fi echo "" # Test admin commands via HTTPS test_config_query echo "" # Test via HTTP test_http_connection echo "" # Test direct connection (bypassing nginx) test_direct_connection echo "" # Test invalid command test_invalid_command echo "" # Test connection persistence test_connection_persistence echo "" echo "==========================================" log_success "WebSocket admin testing complete!" echo "==========================================" echo "" log_info "Summary:" log_info " ✓ WebSocket connection established" log_info " ✓ Kind 23456 events sent" log_info " ✓ Kind 23457 responses received" log_info " ✓ Event signatures verified" echo "" log_warning "NOTE: NIP-44 encryption/decryption not yet implemented in test script" log_warning "Events use plaintext command arrays for initial testing" log_warning "Production implementation uses full NIP-44 encryption" } # Allow sourcing for individual function testing if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi