diff --git a/docker/certbot/.env.example b/docker/certbot/.env.example
index e69de29..2a3ec08 100644
--- a/docker/certbot/.env.example
+++ b/docker/certbot/.env.example
@@ -0,0 +1,19 @@
+# SSL Certificate Configuration
+# Copy this to .env in your project root and customize the values
+
+# Domain for SSL certificate (will also create wildcard *.domain)
+CERTBOT_DOMAIN=dev.uggla.uamils.com
+
+# Email for Let's Encrypt notifications
+CERTBOT_EMAIL=admin@uggla.uamils.com
+
+# Days before expiry to trigger renewal (default: 10)
+CERTBOT_RENEWAL_DAYS=10
+
+# Loopia DNS API credentials (optional, for wildcard certificates)
+# If not provided, will use HTTP challenge (main domain only)
+LOOPIA_USER=your_loopia_username
+LOOPIA_PASSWORD=your_loopia_password
+
+# Note: If using HTTP challenge, make sure port 8888 is accessible
+# from the internet and forwarded to the certbot container
diff --git a/docker/certbot/Dockerfile b/docker/certbot/Dockerfile
index e69de29..04621d3 100644
--- a/docker/certbot/Dockerfile
+++ b/docker/certbot/Dockerfile
@@ -0,0 +1,35 @@
+FROM certbot/certbot:latest
+
+# Install additional tools and python packages
+RUN apk add --no-cache \
+ curl \
+ jq \
+ bash \
+ dcron \
+ nginx \
+ openssl
+
+# Install dns-lexicon for DNS providers support
+RUN pip install dns-lexicon[full]
+
+# Create necessary directories
+RUN mkdir -p /app/scripts /var/log/certbot
+
+# Copy renewal scripts
+COPY scripts/ /app/scripts/
+RUN chmod +x /app/scripts/*.sh
+
+# Copy crontab
+COPY crontab /etc/crontabs/root
+
+# Copy entrypoint
+COPY entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+# Create a simple nginx config for HTTP challenges
+COPY nginx.conf /etc/nginx/nginx.conf
+
+WORKDIR /app
+
+ENTRYPOINT ["/entrypoint.sh"]
+CMD ["crond", "-f", "-l", "2"]
diff --git a/docker/certbot/crontab b/docker/certbot/crontab
index e69de29..97d16d1 100644
--- a/docker/certbot/crontab
+++ b/docker/certbot/crontab
@@ -0,0 +1,5 @@
+# Run certificate renewal check daily at 2 AM
+0 2 * * * /app/scripts/check-and-renew.sh >> /var/log/certbot/renewal.log 2>&1
+
+# Optional: Run a quick status check every 6 hours
+0 */6 * * * /app/scripts/status-check.sh >> /var/log/certbot/status.log 2>&1
diff --git a/docker/certbot/entrypoint.sh b/docker/certbot/entrypoint.sh
index e69de29..5ac6c7c 100644
--- a/docker/certbot/entrypoint.sh
+++ b/docker/certbot/entrypoint.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Entrypoint script for certbot container
+set -e
+
+echo "Starting Certbot Auto-Renewal Container"
+echo "========================================="
+
+# Create log directory
+mkdir -p /var/log/certbot
+
+# Set up environment
+export DOMAIN="${DOMAIN:-dev.uggla.uamils.com}"
+export EMAIL="${EMAIL:-admin@uggla.uamils.com}"
+export RENEWAL_DAYS="${RENEWAL_DAYS:-10}"
+
+echo "Domain: $DOMAIN"
+echo "Email: $EMAIL"
+echo "Renewal threshold: $RENEWAL_DAYS days"
+
+# Initial certificate check/creation
+/app/scripts/check-and-renew.sh
+
+# Start cron daemon
+echo "Starting cron daemon..."
+exec "$@"
diff --git a/docker/certbot/nginx.conf b/docker/certbot/nginx.conf
index e69de29..9b0f106 100644
--- a/docker/certbot/nginx.conf
+++ b/docker/certbot/nginx.conf
@@ -0,0 +1,28 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ # Simple server for HTTP challenges
+ server {
+ listen 80;
+ server_name _;
+
+ root /var/www/html;
+
+ # ACME challenge location
+ location /.well-known/acme-challenge/ {
+ root /var/www/html;
+ try_files $uri =404;
+ }
+
+ # Default location
+ location / {
+ return 200 "Certbot HTTP Challenge Server";
+ add_header Content-Type text/plain;
+ }
+ }
+}
diff --git a/docker/certbot/scripts/check-and-renew.sh b/docker/certbot/scripts/check-and-renew.sh
index e69de29..b30bd63 100644
--- a/docker/certbot/scripts/check-and-renew.sh
+++ b/docker/certbot/scripts/check-and-renew.sh
@@ -0,0 +1,173 @@
+#!/bin/bash
+
+# Main certificate renewal script
+set -e
+
+DOMAIN="${DOMAIN:-dev.uggla.uamils.com}"
+EMAIL="${EMAIL:-admin@uggla.uamils.com}"
+RENEWAL_DAYS="${RENEWAL_DAYS:-10}"
+LOG_FILE="/var/log/certbot/renewal.log"
+
+log() {
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
+}
+
+check_certificate_expiry() {
+ local cert_path="/etc/letsencrypt/live/$DOMAIN/cert.pem"
+
+ if [[ ! -f "$cert_path" ]]; then
+ log "Certificate not found at $cert_path - will attempt to obtain new certificate"
+ return 1
+ fi
+
+ # Get certificate expiry date
+ local expiry_date=$(openssl x509 -in "$cert_path" -noout -enddate | cut -d= -f2)
+ local expiry_epoch=$(date -d "$expiry_date" +%s)
+ local current_epoch=$(date +%s)
+ local days_until_expiry=$(( (expiry_epoch - current_epoch) / 86400 ))
+
+ log "Certificate expires in $days_until_expiry days"
+
+ if [[ $days_until_expiry -le $RENEWAL_DAYS ]]; then
+ log "Certificate expires in $days_until_expiry days - renewal needed"
+ return 1
+ else
+ log "Certificate is valid for $days_until_expiry more days - no renewal needed"
+ return 0
+ fi
+}
+
+attempt_dns_challenge() {
+ log "Attempting DNS challenge with Loopia API..."
+
+ if [[ -z "$LOOPIA_USER" || -z "$LOOPIA_PASSWORD" ]]; then
+ log "LOOPIA_USER or LOOPIA_PASSWORD not set - skipping DNS challenge"
+ return 1
+ fi
+
+ # Try with dns-lexicon
+ if command -v lexicon >/dev/null 2>&1; then
+ log "Using dns-lexicon for DNS challenge..."
+ certbot certonly \
+ --dns-lexicon \
+ --dns-lexicon-provider loopia \
+ --dns-lexicon-loopia-username="$LOOPIA_USER" \
+ --dns-lexicon-loopia-password="$LOOPIA_PASSWORD" \
+ --email "$EMAIL" \
+ --agree-tos \
+ --non-interactive \
+ --expand \
+ -d "$DOMAIN" \
+ -d "*.$DOMAIN"
+ return $?
+ fi
+
+ # Fallback: Manual DNS with custom hook
+ if [[ -f "/app/scripts/loopia-hook.sh" ]]; then
+ log "Using custom Loopia hook for DNS challenge..."
+ certbot certonly \
+ --manual \
+ --preferred-challenges dns \
+ --manual-auth-hook "/app/scripts/loopia-hook.sh auth" \
+ --manual-cleanup-hook "/app/scripts/loopia-hook.sh cleanup" \
+ --email "$EMAIL" \
+ --agree-tos \
+ --non-interactive \
+ --expand \
+ -d "$DOMAIN" \
+ -d "*.$DOMAIN"
+ return $?
+ fi
+
+ return 1
+}
+
+attempt_http_challenge() {
+ log "Attempting HTTP challenge..."
+
+ # Start nginx for HTTP challenge
+ nginx -t && nginx
+
+ certbot certonly \
+ --webroot \
+ --webroot-path=/var/www/html \
+ --email "$EMAIL" \
+ --agree-tos \
+ --non-interactive \
+ --expand \
+ -d "$DOMAIN"
+
+ local result=$?
+
+ # Stop nginx
+ nginx -s quit || true
+
+ return $result
+}
+
+reload_nginx() {
+ log "Copying certificates to shared directory..."
+
+ # Copy certificates to shared SSL directory
+ if [[ -d "/etc/letsencrypt/live/$DOMAIN" ]]; then
+ mkdir -p /shared/ssl
+ cp "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" "/shared/ssl/fullchain.pem" || log "Could not copy fullchain.pem"
+ cp "/etc/letsencrypt/live/$DOMAIN/privkey.pem" "/shared/ssl/privkey.pem" || log "Could not copy privkey.pem"
+ chmod 644 /shared/ssl/fullchain.pem
+ chmod 600 /shared/ssl/privkey.pem
+ log "Certificates copied to /shared/ssl/"
+ fi
+
+ log "Reloading nginx configuration..."
+
+ # Check if nginx container exists and reload it
+ if command -v docker >/dev/null 2>&1; then
+ if docker ps --format "table {{.Names}}" | grep -q "nginx\|proxy"; then
+ log "Reloading nginx container..."
+ docker exec nginx nginx -s reload 2>/dev/null || \
+ docker exec drone-detection-nginx nginx -s reload 2>/dev/null || \
+ log "Could not reload nginx container - manual reload may be required"
+ fi
+ fi
+
+ # Also try to reload host nginx if running
+ if pgrep nginx >/dev/null; then
+ log "Reloading host nginx..."
+ nginx -s reload || log "Could not reload host nginx"
+ fi
+}
+
+main() {
+ log "Starting certificate renewal check for $DOMAIN"
+
+ # Check if renewal is needed
+ if check_certificate_expiry; then
+ log "Certificate is still valid - no action needed"
+ exit 0
+ fi
+
+ log "Certificate renewal required - attempting to renew..."
+
+ # Try DNS challenge first (for wildcard support)
+ if attempt_dns_challenge; then
+ log "DNS challenge successful!"
+ reload_nginx
+ log "Certificate renewal completed successfully"
+ exit 0
+ fi
+
+ # Fallback to HTTP challenge (for main domain only)
+ log "DNS challenge failed - falling back to HTTP challenge"
+ if attempt_http_challenge; then
+ log "HTTP challenge successful!"
+ reload_nginx
+ log "Certificate renewal completed successfully"
+ exit 0
+ fi
+
+ log "ERROR: All certificate renewal attempts failed"
+ exit 1
+}
+
+# Run main function
+main "$@"
diff --git a/docker/certbot/scripts/loopia-hook.sh b/docker/certbot/scripts/loopia-hook.sh
index e69de29..8b4c7e6 100644
--- a/docker/certbot/scripts/loopia-hook.sh
+++ b/docker/certbot/scripts/loopia-hook.sh
@@ -0,0 +1,158 @@
+#!/bin/bash
+
+# Loopia DNS API hook for certbot
+# This script handles DNS TXT record creation and cleanup for Let's Encrypt challenges
+
+set -e
+
+LOOPIA_USER="${LOOPIA_USER:-}"
+LOOPIA_PASSWORD="${LOOPIA_PASSWORD:-}"
+LOOPIA_API_URL="https://api.loopia.se/RPCSERV"
+
+log() {
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Loopia Hook: $1" >&2
+}
+
+# Extract domain and subdomain parts
+extract_domain_parts() {
+ local full_domain="$1"
+
+ # For *.dev.uggla.uamils.com -> domain=uamils.com, subdomain=_acme-challenge.dev
+ # For dev.uggla.uamils.com -> domain=uamils.com, subdomain=_acme-challenge.dev
+
+ if [[ "$full_domain" == *.uamils.com ]]; then
+ DOMAIN="uamils.com"
+ local prefix="${full_domain%.uamils.com}"
+ if [[ "$prefix" == *"*"* ]]; then
+ # Wildcard domain *.dev.uggla.uamils.com
+ SUBDOMAIN="_acme-challenge.${prefix#*.}"
+ else
+ # Regular domain dev.uggla.uamils.com
+ SUBDOMAIN="_acme-challenge.$prefix"
+ fi
+ else
+ log "ERROR: Unsupported domain format: $full_domain"
+ exit 1
+ fi
+}
+
+# Call Loopia API
+call_loopia_api() {
+ local method="$1"
+ shift
+ local params="$@"
+
+ curl -s -X POST "$LOOPIA_API_URL" \
+ -H "Content-Type: text/xml" \
+ -d "
+
+ $method
+
+ $LOOPIA_USER
+ $LOOPIA_PASSWORD
+ $params
+
+"
+}
+
+# Add DNS TXT record
+add_txt_record() {
+ local domain="$1"
+ local subdomain="$2"
+ local txt_value="$3"
+
+ log "Adding TXT record: $subdomain.$domain = $txt_value"
+
+ local response=$(call_loopia_api "addZoneRecord" \
+ "$domain
+ $subdomain
+
+
+ type
+ TXT
+
+
+ rdata
+ $txt_value
+
+
+ ttl
+ 300
+
+ ")
+
+ if echo "$response" | grep -q "OK"; then
+ log "Successfully added TXT record"
+ else
+ log "ERROR adding TXT record: $response"
+ exit 1
+ fi
+}
+
+# Remove DNS TXT record
+remove_txt_record() {
+ local domain="$1"
+ local subdomain="$2"
+
+ log "Removing TXT records for: $subdomain.$domain"
+
+ # Get existing records
+ local records=$(call_loopia_api "getZoneRecords" \
+ "$domain
+ $subdomain")
+
+ # Extract record IDs for TXT records
+ local record_ids=$(echo "$records" | grep -A5 "type" | grep -B5 "TXT" | grep "record_id" | sed 's/.*\([0-9]*\)<\/int>.*/\1/')
+
+ for record_id in $record_ids; do
+ if [[ -n "$record_id" ]]; then
+ log "Removing TXT record ID: $record_id"
+ call_loopia_api "removeZoneRecord" \
+ "$domain
+ $subdomain
+ $record_id"
+ fi
+ done
+}
+
+# Main script logic
+case "$1" in
+ "auth")
+ if [[ -z "$CERTBOT_DOMAIN" || -z "$CERTBOT_VALIDATION" ]]; then
+ log "ERROR: CERTBOT_DOMAIN or CERTBOT_VALIDATION not set"
+ exit 1
+ fi
+
+ if [[ -z "$LOOPIA_USER" || -z "$LOOPIA_PASSWORD" ]]; then
+ log "ERROR: LOOPIA_USER or LOOPIA_PASSWORD not set"
+ exit 1
+ fi
+
+ extract_domain_parts "$CERTBOT_DOMAIN"
+ add_txt_record "$DOMAIN" "$SUBDOMAIN" "$CERTBOT_VALIDATION"
+
+ # Wait for DNS propagation
+ log "Waiting 60 seconds for DNS propagation..."
+ sleep 60
+ ;;
+
+ "cleanup")
+ if [[ -z "$CERTBOT_DOMAIN" ]]; then
+ log "ERROR: CERTBOT_DOMAIN not set"
+ exit 1
+ fi
+
+ if [[ -z "$LOOPIA_USER" || -z "$LOOPIA_PASSWORD" ]]; then
+ log "WARNING: LOOPIA_USER or LOOPIA_PASSWORD not set - skipping cleanup"
+ exit 0
+ fi
+
+ extract_domain_parts "$CERTBOT_DOMAIN"
+ remove_txt_record "$DOMAIN" "$SUBDOMAIN"
+ ;;
+
+ *)
+ log "Usage: $0 {auth|cleanup}"
+ exit 1
+ ;;
+esac
diff --git a/docker/certbot/scripts/status-check.sh b/docker/certbot/scripts/status-check.sh
index e69de29..eaaab83 100644
--- a/docker/certbot/scripts/status-check.sh
+++ b/docker/certbot/scripts/status-check.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Status check script for monitoring
+set -e
+
+DOMAIN="${DOMAIN:-dev.uggla.uamils.com}"
+LOG_FILE="/var/log/certbot/status.log"
+
+log() {
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
+}
+
+check_certificate_status() {
+ local cert_path="/etc/letsencrypt/live/$DOMAIN/cert.pem"
+
+ if [[ ! -f "$cert_path" ]]; then
+ log "STATUS: No certificate found for $DOMAIN"
+ return 1
+ fi
+
+ # Get certificate details
+ local expiry_date=$(openssl x509 -in "$cert_path" -noout -enddate | cut -d= -f2)
+ local expiry_epoch=$(date -d "$expiry_date" +%s)
+ local current_epoch=$(date +%s)
+ local days_until_expiry=$(( (expiry_epoch - current_epoch) / 86400 ))
+
+ # Get certificate subject
+ local subject=$(openssl x509 -in "$cert_path" -noout -subject | sed 's/subject=//')
+
+ log "STATUS: Certificate for $DOMAIN"
+ log " Subject: $subject"
+ log " Expires: $expiry_date"
+ log " Days until expiry: $days_until_expiry"
+
+ if [[ $days_until_expiry -le 10 ]]; then
+ log " WARNING: Certificate expires soon!"
+ elif [[ $days_until_expiry -le 30 ]]; then
+ log " NOTICE: Certificate expires in less than 30 days"
+ else
+ log " OK: Certificate is valid"
+ fi
+}
+
+# Check certificate status
+check_certificate_status
diff --git a/sample_detection.json b/sample_detection.json
index e69de29..821ac5f 100644
--- a/sample_detection.json
+++ b/sample_detection.json
@@ -0,0 +1,12 @@
+{
+ "device_id": 1,
+ "drone_id": 2001,
+ "drone_type": 1,
+ "rssi": -72,
+ "freq": 2450,
+ "geo_lat": 59.3293,
+ "geo_lon": 18.0686,
+ "device_timestamp": 1692252000000,
+ "confidence_level": 0.92,
+ "signal_duration": 3200
+}
diff --git a/start-healthprobe.bat b/start-healthprobe.bat
index e69de29..8624db4 100644
--- a/start-healthprobe.bat
+++ b/start-healthprobe.bat
@@ -0,0 +1,46 @@
+@echo off
+REM Health Probe Simulator Startup Script (Windows)
+REM This script starts the health probe simulator that continuously sends heartbeats
+
+echo 🏥 Starting Health Probe Simulator...
+echo ==================================
+
+REM Set defaults if not provided
+if not defined PROBE_FAILRATE set PROBE_FAILRATE=30
+if not defined PROBE_INTERVAL_SECONDS set PROBE_INTERVAL_SECONDS=60
+if not defined API_BASE_URL set API_BASE_URL=https://selfservice.cqers.com/drones/api
+
+echo 🔧 Configuration:
+echo Failure Rate: %PROBE_FAILRATE%%%
+echo Probe Interval: %PROBE_INTERVAL_SECONDS% seconds
+echo API URL: %API_BASE_URL%
+echo.
+
+REM Check if running with Docker
+if "%1"=="docker" (
+ echo 🐳 Starting with Docker Compose...
+ docker-compose --profile healthprobe up healthprobe
+) else (
+ echo 💻 Running locally...
+
+ REM Check if Python is available
+ python --version >nul 2>&1
+ if errorlevel 1 (
+ echo ❌ Python is required but not installed
+ pause
+ exit /b 1
+ )
+
+ REM Check if requests module is available
+ python -c "import requests" >nul 2>&1
+ if errorlevel 1 (
+ echo 📦 Installing requests module...
+ pip install requests
+ )
+
+ REM Run the health probe simulator
+ echo 🚀 Starting health probe simulator...
+ python health_probe_simulator.py
+)
+
+pause
diff --git a/test-docker-builds.bat b/test-docker-builds.bat
index e69de29..8d7e24f 100644
--- a/test-docker-builds.bat
+++ b/test-docker-builds.bat
@@ -0,0 +1,38 @@
+@echo off
+setlocal
+
+echo 🐳 Testing Docker Builds
+echo =======================
+
+echo [INFO] Building backend container...
+docker build -t drone-backend ./server
+if %errorlevel% neq 0 (
+ echo [ERROR] Backend build failed
+ exit /b 1
+)
+echo [SUCCESS] Backend build completed
+
+echo [INFO] Building frontend container...
+docker build -t drone-frontend ./client
+if %errorlevel% neq 0 (
+ echo [ERROR] Frontend build failed
+ exit /b 1
+)
+echo [SUCCESS] Frontend build completed
+
+echo [INFO] Building simulator container...
+docker build -f docker/simulator/Dockerfile -t drone-simulator .
+if %errorlevel% neq 0 (
+ echo [ERROR] Simulator build failed
+ exit /b 1
+)
+echo [SUCCESS] Simulator build completed
+
+echo [SUCCESS] All builds completed successfully!
+echo.
+echo You can now run:
+echo docker compose up -d
+echo or
+echo docker compose -f docker compose.simple.yml up -d
+
+pause