summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordoc <doc@filenotfound.org>2025-09-16 11:30:38 +0000
committerdoc <doc@filenotfound.org>2025-09-16 11:30:38 +0000
commit5cfeb2cf156705349730bdc6ce883fd8eb22e728 (patch)
tree13758f78513ed74198d2257c11c0ef9a913725d1
first commitHEADmaster
-rw-r--r--create.sh34
-rwxr-xr-xfortress.sh31
-rwxr-xr-xlxd_auto_snap.sh21
-rwxr-xr-xlxdbackup.sh43
-rwxr-xr-xmove_containers.sh44
-rwxr-xr-xrebootcheck.sh26
-rwxr-xr-xrecid.sh3
-rwxr-xr-xreorder_override.sh30
-rwxr-xr-xreorder_validation.sh45
-rwxr-xr-xreorderips.sh53
-rwxr-xr-xsqueakyclean.sh36
-rwxr-xr-xsync_tt_to_alt.sh14
-rwxr-xr-xsync_tt_to_minio.sh37
-rwxr-xr-xtestdm.sh11
-rwxr-xr-xttbanwatch.sh79
-rwxr-xr-xwatchman.sh40
16 files changed, 547 insertions, 0 deletions
diff --git a/create.sh b/create.sh
new file mode 100644
index 0000000..eac4147
--- /dev/null
+++ b/create.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+echo "πŸš€ LXD New Container Setup Script"
+
+read -p "πŸ“ Enter the new container name: " name
+read -p "🌐 Enter static IP to assign (e.g. 10.196.1.70): " ip
+
+echo "πŸ”§ Launching new Ubuntu 22.04 container: $name"
+lxc launch ubuntu:22.04 "$name"
+
+echo "πŸ”§ Overriding eth0 device to allow static IP assignment..."
+lxc config device override "$name" eth0
+
+echo "πŸ”§ Assigning static IP: $ip"
+lxc config device set "$name" eth0 ipv4.address "$ip"
+
+echo "πŸ”„ Restarting $name to apply new IP..."
+lxc restart "$name"
+
+echo "πŸ› οΈ Validating IP and hostname inside the container..."
+actual_ip=$(lxc exec "$name" -- hostname -I | awk '{print $1}')
+hostname_inside=$(lxc exec "$name" -- hostname)
+
+echo "βœ… Container '$name' has been assigned IP: $actual_ip"
+echo "🌐 Container DNS hostname: $hostname_inside"
+
+ping -c 2 "$ip" > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ echo "βœ… Ping to $ip successful!"
+else
+ echo "❌ Ping to $ip failed!"
+fi
+
+echo "πŸŽ‰ Container setup complete!"
diff --git a/fortress.sh b/fortress.sh
new file mode 100755
index 0000000..4df0921
--- /dev/null
+++ b/fortress.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# fortress_setup_ubuntu.sh - secure SSH fortress on Ubuntu with UFW
+
+set -e
+
+bouncer_ip="38.102.127.173"
+
+echo "Updating package lists..."
+sudo apt update
+
+echo "Installing UFW if not present..."
+sudo apt install -y ufw
+
+echo "Setting default UFW policies..."
+sudo ufw default deny incoming
+sudo ufw default allow outgoing
+
+echo "Allowing SSH only from bouncer ($bouncer_ip)..."
+sudo ufw allow from $bouncer_ip to any port 22 proto tcp comment 'Allow SSH from bouncer'
+
+echo "Allowing inbound HTTP/HTTPS..."
+sudo ufw allow 80/tcp comment 'Allow HTTP'
+sudo ufw allow 443/tcp comment 'Allow HTTPS'
+
+echo "Enabling UFW..."
+sudo ufw --force enable
+
+echo "UFW Status:"
+sudo ufw status verbose
+
+echo "SSH fortress is active! Only $bouncer_ip can connect via SSH."
diff --git a/lxd_auto_snap.sh b/lxd_auto_snap.sh
new file mode 100755
index 0000000..fa87eaa
--- /dev/null
+++ b/lxd_auto_snap.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+echo "πŸ”„ Starting automatic LXD snapshot for all running containers..."
+
+# Get a list of running container names
+containers=$(lxc list --format csv -c ns | awk -F, '$2 == "RUNNING" {print $1}')
+
+if [[ -z "$containers" ]]; then
+ echo "❌ No running containers found!"
+ exit 1
+fi
+
+# Create a snapshot for each container
+timestamp=$(date +%Y%m%d-%H%M%S)
+for container in $containers; do
+ snapshot_name="auto-${timestamp}"
+ echo "🟑 Creating snapshot for $container: $snapshot_name"
+ lxc snapshot "$container" "$snapshot_name"
+done
+
+echo "βœ… Snapshot creation complete!"
diff --git a/lxdbackup.sh b/lxdbackup.sh
new file mode 100755
index 0000000..8865219
--- /dev/null
+++ b/lxdbackup.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# backup-lxd.sh - Full LXD backup with offsite rsync
+
+NOW=$(date +'%F')
+BACKUP_DIR="/backups/lxd"
+REMOTE_USER="doc"
+REMOTE_HOST="thevault.bounceme.net"
+REMOTE_PATH="/gigapool/lxdbackups/${NOW}" # Subdir per date
+
+echo "πŸ“¦ Starting LXD backup @ $NOW"
+mkdir -p "$BACKUP_DIR"
+
+# Backup LXD server config
+echo "πŸ› οΈ Dumping LXD init config..."
+lxd init --dump > "${BACKUP_DIR}/lxd.config.${NOW}"
+
+# Save instance list and version info
+echo "πŸ“‹ Capturing metadata..."
+lxc list > "${BACKUP_DIR}/lxd.instances.list.${NOW}"
+snap list lxd > "${BACKUP_DIR}/lxd-version.${NOW}"
+
+# Export all LXD containers
+echo "πŸ“¦ Backing up LXD instances..."
+for INSTANCE in $(lxc list -c n --format csv); do
+ echo "➑️ Exporting $INSTANCE..."
+ lxc export "$INSTANCE" "${BACKUP_DIR}/${INSTANCE}-backup-${NOW}.tar.xz" --optimized-storage \
+ && echo "βœ… Done: $INSTANCE" \
+ || echo "❌ Failed: $INSTANCE"
+done
+
+# Offsite rsync to thevault
+echo "πŸš€ Syncing to ${REMOTE_HOST}:${REMOTE_PATH} ..."
+rsync -avh --progress --delete \
+ "$BACKUP_DIR/" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/"
+
+if [[ $? -eq 0 ]]; then
+ echo "βœ… Offsite sync completed successfully."
+else
+ echo "❌ Rsync failed! Check connection or credentials."
+ exit 1
+fi
+
+echo "πŸŽ‰ LXD backup + offsite sync complete."
diff --git a/move_containers.sh b/move_containers.sh
new file mode 100755
index 0000000..7062177
--- /dev/null
+++ b/move_containers.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+# === Config ===
+CONTAINERS=(
+ archivecontrol
+ archivelist
+ ftp
+ hostingtoot
+ humptydumpty
+ teamtalk
+ akkoma
+)
+
+TARGET_POOL="infernode-pool"
+
+# === Move loop ===
+for container in "${CONTAINERS[@]}"; do
+ echo "=== Processing container: $container ==="
+
+ echo "Stopping $container..."
+ lxc stop "$container" --timeout=30
+
+ echo "Waiting for $container to fully shut down..."
+ while true; do
+ status=$(lxc info "$container" | grep "Status:" | awk '{print $2}')
+ if [[ "$status" != "RUNNING" ]]; then
+ echo "$container is fully stopped."
+ break
+ fi
+ echo "$container still stopping... waiting 3 more seconds."
+ sleep 3
+ done
+
+ echo "Moving $container to pool: $TARGET_POOL"
+ lxc move "$container" "$container" --storage "$TARGET_POOL"
+
+ echo "Starting $container..."
+ lxc start "$container"
+
+ echo "=== $container moved and restarted! ==="
+ echo
+done
+
+echo "=== All containers have been successfully moved and restarted! ==="
diff --git a/rebootcheck.sh b/rebootcheck.sh
new file mode 100755
index 0000000..62074f5
--- /dev/null
+++ b/rebootcheck.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# genesisctl-reboot-check - Lightweight reboot health checker
+
+LAST_BOOT=$(uptime -s 2>/dev/null)
+LAST_BOOT_TS=$(date -d "$LAST_BOOT" +%s)
+NOW_TS=$(date +%s)
+DAYS_SINCE_BOOT=$(( (NOW_TS - LAST_BOOT_TS) / 86400 ))
+
+REBOOT_REQUIRED_FILE="/var/run/reboot-required"
+
+echo "🧠 Genesis Reboot Check"
+echo "-----------------------"
+echo "πŸ•’ Last Boot: $LAST_BOOT"
+echo "πŸ“† Days Since Boot: $DAYS_SINCE_BOOT"
+
+if [ "$DAYS_SINCE_BOOT" -gt 30 ]; then
+ echo "⚠️ Overdue: It's been more than 30 days since your last reboot."
+else
+ echo "βœ… Reboot not overdue (monthly policy)"
+fi
+
+if [ -f "$REBOOT_REQUIRED_FILE" ]; then
+ echo "⚠️ System indicates a reboot is required (/var/run/reboot-required exists)"
+else
+ echo "βœ… No reboot required by the system"
+fi
diff --git a/recid.sh b/recid.sh
new file mode 100755
index 0000000..6a7ff6c
--- /dev/null
+++ b/recid.sh
@@ -0,0 +1,3 @@
+curl -X GET "https://api.cloudflare.com/client/v4/zones/c5099d42caa2d9763227267c597cb758/dns_records" \
+ -H "Authorization: Bearer lCz1kH6nBZPJL0EWrNI-xEDwfR0oOLpg05fq6M81" \
+ -H "Content-Type: application/json" | jq
diff --git a/reorder_override.sh b/reorder_override.sh
new file mode 100755
index 0000000..35edb2f
--- /dev/null
+++ b/reorder_override.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+echo "Reassigning container IPs in increments of 10..."
+
+declare -A CONTAINERS
+CONTAINERS=(
+ [archivecontrol]="10.196.1.10"
+ [archivelist]="10.196.1.20"
+ [ftp]="10.196.1.30"
+ [hostingtoot]="10.196.1.40"
+ [humptydumpty]="10.196.1.50"
+ [teamtalk]="10.196.1.60"
+)
+
+for container in "${!CONTAINERS[@]}"; do
+ new_ip="${CONTAINERS[$container]}"
+ echo "➑️ Overriding eth0 device for $container..."
+ lxc config device override "$container" eth0
+ echo "πŸ”§ Setting $container to IP $new_ip..."
+ lxc config device set "$container" eth0 ipv4.address "$new_ip"
+done
+
+echo "πŸ”„ Restarting containers to apply new IPs..."
+for container in "${!CONTAINERS[@]}"; do
+ echo "πŸ”„ Restarting $container..."
+ lxc restart "$container"
+done
+
+echo "βœ… IP assignment complete! Here’s the new layout:"
+lxc list
diff --git a/reorder_validation.sh b/reorder_validation.sh
new file mode 100755
index 0000000..29de3f3
--- /dev/null
+++ b/reorder_validation.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+echo "πŸ” Starting IP validation and connectivity tests..."
+
+declare -A CONTAINERS
+CONTAINERS=(
+ [archivecontrol]="10.196.1.10"
+ [archivelist]="10.196.1.20"
+ [ftp]="10.196.1.30"
+ [hostingtoot]="10.196.1.40"
+ [humptydumpty]="10.196.1.50"
+ [teamtalk]="10.196.1.60"
+ [akkoma]="10.196.1.70"
+)
+
+for container in "${!CONTAINERS[@]}"; do
+ expected_ip="${CONTAINERS[$container]}"
+ echo "πŸ”Ž Checking $container..."
+
+ # Get actual IP from inside container
+ actual_ip=$(lxc exec "$container" -- hostname -I | awk '{print $1}')
+
+ # Compare expected vs actual IP
+ if [[ "$expected_ip" == "$actual_ip" ]]; then
+ echo "βœ… $container IP matches expected: $expected_ip"
+ else
+ echo "❌ $container IP mismatch! Expected: $expected_ip, Found: $actual_ip"
+ fi
+
+ # Ping test from host
+ echo "πŸ“‘ Pinging $expected_ip..."
+ ping -c 2 "$expected_ip" > /dev/null 2>&1
+ if [[ $? -eq 0 ]]; then
+ echo "βœ… Ping to $expected_ip successful"
+ else
+ echo "❌ Ping to $expected_ip failed"
+ fi
+
+ # DNS hostname inside container
+ container_hostname=$(lxc exec "$container" -- hostname)
+ echo "🌐 $container DNS hostname: $container_hostname"
+ echo "-------------------------------------------"
+done
+
+echo "🌈 Validation and connectivity tests complete!"
diff --git a/reorderips.sh b/reorderips.sh
new file mode 100755
index 0000000..6567d95
--- /dev/null
+++ b/reorderips.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+echo "Reassigning container IPs in increments of 10..."
+
+# Container name to IP mapping
+declare -A CONTAINERS=(
+ [archivecontrol]="10.196.1.10"
+ [archivelist]="10.196.1.20"
+ [ftp]="10.196.1.30"
+ [hostingtoot]="10.196.1.40"
+ [humptydumpty]="10.196.1.50"
+ [teamtalk]="10.196.1.60"
+ [akkoma]="10.196.1.70"
+)
+
+# Ordered list to process in sequence
+ORDER=(archivecontrol archivelist ftp hostingtoot humptydumpty teamtalk akkoma)
+
+for container in "${ORDER[@]}"; do
+ new_ip="${CONTAINERS[$container]}"
+
+ # Check current IP assignment
+ current_ip=$(lxc config device get "$container" eth0 ipv4.address 2>/dev/null)
+
+ if [[ "$current_ip" == "$new_ip" ]]; then
+ # No change needed
+ continue
+ fi
+
+ echo "Updating $container: assigning IP $new_ip (was: ${current_ip:-unset})"
+
+ # Override NIC to allow per-container IP (silently if already done)
+ lxc config device override "$container" eth0 >/dev/null 2>&1
+
+ # Clear any existing IP config
+ lxc config device unset "$container" eth0 ipv4.address >/dev/null 2>&1
+
+ # Set the new IP
+ lxc config device set "$container" eth0 ipv4.address "$new_ip"
+done
+
+echo "Restarting updated containers..."
+for container in "${ORDER[@]}"; do
+ new_ip="${CONTAINERS[$container]}"
+ current_ip=$(lxc config device get "$container" eth0 ipv4.address 2>/dev/null)
+ if [[ "$current_ip" == "$new_ip" ]]; then
+ echo "Restarting $container (new IP: $new_ip)"
+ lxc restart "$container" >/dev/null
+ fi
+done
+
+echo "IP assignment complete! Here’s the final layout:"
+lxc list
diff --git a/squeakyclean.sh b/squeakyclean.sh
new file mode 100755
index 0000000..bb1e754
--- /dev/null
+++ b/squeakyclean.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+echo "=== LXD Storage Pool Check and Cleanup Helper ==="
+
+# List all containers with their active storage pool
+echo
+echo "Container -> Storage Pool:"
+for container in $(lxc list --format csv -c n); do
+ pool=$(lxc config device get "$container" root pool 2>/dev/null)
+ echo "$container -> ${pool:-default}"
+done
+
+echo
+echo "=== Checking for leftover partial volumes in /mnt/infernode_lxd (NFS) ==="
+if mountpoint -q /mnt/infernode_lxd; then
+ leftovers=$(find /mnt/infernode_lxd -mindepth 1 -maxdepth 1)
+ if [[ -z "$leftovers" ]]; then
+ echo "No leftover volumes found. Clean as a whistle!"
+ else
+ echo "Found leftover items:"
+ echo "$leftovers"
+ echo
+ read -p "Do you want to remove these? (y/n) " confirm
+ if [[ "$confirm" == "y" ]]; then
+ sudo rm -rf /mnt/infernode_lxd/*
+ echo "Leftovers removed!"
+ else
+ echo "Leaving leftovers untouched for now."
+ fi
+ fi
+else
+ echo "NFS mount not found at /mnt/infernode_lxd. Skipping leftover check."
+fi
+
+echo
+echo "=== Done! ==="
diff --git a/sync_tt_to_alt.sh b/sync_tt_to_alt.sh
new file mode 100755
index 0000000..1d94d4a
--- /dev/null
+++ b/sync_tt_to_alt.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# === Sync /home/tt from Montreal to Linode ===
+
+# Vars
+SOURCE="/home/tt/"
+DESTINATION="root@172.238.63.162:/home/tt/"
+LOG_FILE="/var/log/tt_sync.log"
+DATE=$(date '+%Y-%m-%d %H:%M:%S')
+
+# Rsync command - preserves perms, ownership, etc.
+rsync -az --delete "$SOURCE" "$DESTINATION" >> "$LOG_FILE" 2>&1
+
+# Logging
+echo "[$DATE] Sync completed" >> "$LOG_FILE"
diff --git a/sync_tt_to_minio.sh b/sync_tt_to_minio.sh
new file mode 100755
index 0000000..75512ce
--- /dev/null
+++ b/sync_tt_to_minio.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# === CONFIG ===
+MINIO_HOST="shredder.sshjunkie.com" # Use IP if DNS doesn't resolve
+MINIO_PORT="9000"
+ACCESS_KEY="genesisadmin"
+SECRET_KEY="MutationXv3!"
+BUCKET="teamtalkdata"
+SOURCE_DIR="/home/tt"
+LOG_FILE="/var/log/tt_sync.log"
+MC_BIN="/usr/local/bin/mc" # Change this path if mc is installed elsewhere
+TELEGRAM_BOT_TOKEN="YOUR_TOKEN_HERE"
+TELEGRAM_CHAT_ID="YOUR_CHAT_ID_HERE"
+
+# === TIMESTAMP ===
+TS=$(date '+%Y-%m-%d %H:%M:%S')
+echo "[$TS] Starting TeamTalk sync..." >> "$LOG_FILE"
+
+# === SETUP ALIAS ===
+$MC_BIN alias set local "http://${MINIO_HOST}:${MINIO_PORT}" "$ACCESS_KEY" "$SECRET_KEY" 2>> "$LOG_FILE"
+
+# === SYNC ===
+$MC_BIN mirror "$SOURCE_DIR" "local/${BUCKET}" --overwrite --remove --quiet 2>> "$LOG_FILE"
+STATUS=$?
+
+if [ $STATUS -eq 0 ]; then
+ echo "[$TS] βœ… Sync completed successfully." >> "$LOG_FILE"
+ curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
+ -d chat_id="${TELEGRAM_CHAT_ID}" \
+ -d text="βœ… TeamTalk sync to MinIO completed at $TS"
+else
+ echo "[$TS] ❌ Sync failed with exit code $STATUS." >> "$LOG_FILE"
+ curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
+ -d chat_id="${TELEGRAM_CHAT_ID}" \
+ -d text="❌ TeamTalk sync to MinIO failed at $TS (exit code $STATUS)"
+fi
+
diff --git a/testdm.sh b/testdm.sh
new file mode 100755
index 0000000..e44be4a
--- /dev/null
+++ b/testdm.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+MASTODON_URL="https://chatwithus.live"
+ACCESS_TOKEN="DsusOr2Gr6UN-sgssHD1XOJr5q9eCbe95fYxUDPM9EQ"
+
+MESSAGE="πŸ”” This is a test direct message from TeamTalk security script at $(hostname)!"
+
+curl -s -X POST "$MASTODON_URL/api/v1/statuses" \
+ -H "Authorization: Bearer $ACCESS_TOKEN" \
+ -F "status=$MESSAGE" \
+ -F "visibility=direct" | jq .
diff --git a/ttbanwatch.sh b/ttbanwatch.sh
new file mode 100755
index 0000000..7680366
--- /dev/null
+++ b/ttbanwatch.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+# === Config ===
+LOG="/var/log/fail2ban.log"
+MASTODON_URL="https://chatwithus.live"
+MASTODON_TOKEN="DsusOr2Gr6UN-sgssHD1XOJr5q9eCbe95fYxUDPM9EQ"
+
+BAN_CACHE="/var/tmp/teamtalk_ban_cache.txt"
+UNBAN_CACHE="/var/tmp/teamtalk_unban_cache.txt"
+TTL=604800 # 7 days in seconds
+
+# === Functions ===
+
+send_dm() {
+ curl -s -X POST "$MASTODON_URL/api/v1/statuses" \
+ -H "Authorization: Bearer $MASTODON_TOKEN" \
+ -F "status=$1" \
+ -F "visibility=direct" > /dev/null
+}
+
+send_log_post() {
+ curl -s -X POST "$MASTODON_URL/api/v1/statuses" \
+ -H "Authorization: Bearer $MASTODON_TOKEN" \
+ -F "status=$1" \
+ -F "visibility=unlisted" > /dev/null
+}
+
+cleanup_cache() {
+ local cache_file="$1"
+ local now=$(date +%s)
+ local temp_file=$(mktemp)
+
+ while IFS="|" read -r ip timestamp; do
+ [[ -z "$ip" || -z "$timestamp" ]] && continue
+ if (( now - timestamp < TTL )); then
+ echo "$ip|$timestamp" >> "$temp_file"
+ fi
+ done < "$cache_file"
+
+ mv "$temp_file" "$cache_file"
+}
+
+log_recent_ips() {
+ local keyword="$1" # Ban or Unban
+ grep "$keyword" "$LOG" | grep 'teamtalk' | awk -v now="$(date +%s)" '
+ {
+ cmd = "date +%s -d \"" $1 " " $2 "\""
+ cmd | getline timestamp
+ close(cmd)
+ if ((now - timestamp) <= 3600) {
+ print $NF
+ }
+ }
+ ' | sort -u
+}
+
+# === Init Caches ===
+touch "$BAN_CACHE"
+touch "$UNBAN_CACHE"
+cleanup_cache "$BAN_CACHE"
+cleanup_cache "$UNBAN_CACHE"
+
+# === Process Bans ===
+log_recent_ips "Ban" | while read ip; do
+ if ! grep -q "^$ip|" "$BAN_CACHE"; then
+ echo "$ip|$(date +%s)" >> "$BAN_CACHE"
+ send_dm "🚫 GenesisShield TeamTalk Fail2Ban alert: Banned IP $ip"
+ send_log_post "🚫 #GenesisShield TeamTalk Banned IP $ip at $(date -Is)"
+ fi
+done
+
+# === Process Unbans ===
+log_recent_ips "Unban" | while read ip; do
+ if ! grep -q "^$ip|" "$UNBAN_CACHE"; then
+ echo "$ip|$(date +%s)" >> "$UNBAN_CACHE"
+ send_dm "βœ… TeamTalk Fail2Ban alert: IP $ip has been unbanned"
+ send_log_post "βœ… #GenesisShield TeamTalk Unbanned IP $ip at $(date -Is)"
+ fi
+done
diff --git a/watchman.sh b/watchman.sh
new file mode 100755
index 0000000..085fd0d
--- /dev/null
+++ b/watchman.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# === Your actual setup ===
+HOST="38.102.127.168" # Montreal's IP to probe
+THRESHOLD_LATENCY=150 # in ms
+THRESHOLD_LOSS=5 # in %
+CF_ZONE_ID="c5099d42caa2d9763227267c597cb758" # Replace with your real Cloudflare zone ID
+CF_RECORD_ID="7001484a25f0fe5c323845b6695f7544" # Replace with your real DNS record ID
+CF_API_TOKEN="lCz1kH6nBZPJL0EWrNI-xEDwfR0oOLpg05fq6M81" # Cloudflare API token
+LINODE_IP="172.238.63.162" # Linode IP
+LOG_FILE="/var/log/tt_failover.log"
+DATE=$(date '+%Y-%m-%d %H:%M:%S')
+
+# === Run mtr probe ===
+mtr -r -c 10 $HOST > /tmp/mtr_output.txt
+
+# === Extract average latency and packet loss ===
+# Average latency from ping (use 10 pings)
+AVG_LATENCY=$(ping -c 10 $HOST | tail -1| awk '{print $4}' | cut -d '/' -f 2)
+
+# Packet loss from ping (last line)
+LOSS=$(ping -c 10 $HOST | grep -oP '\d+(?=% packet loss)')
+
+echo "[$DATE] DEBUG: AVG_LATENCY=$AVG_LATENCY, LOSS=$LOSS" >> $LOG_FILE
+
+
+# === Logging ===
+echo "[$DATE] Latency: $AVG_LATENCY ms, Loss: $LOSS%" >> $LOG_FILE
+
+# === Decision ===
+if (( $(echo "$AVG_LATENCY > $THRESHOLD_LATENCY" | bc -l) )) || (( $(echo "$LOSS > $THRESHOLD_LOSS" | bc -l) )); then
+ echo "[$DATE] πŸ”΄ Montreal is glitchy! Flipping to Linode ($LINODE_IP)..." >> $LOG_FILE
+ curl -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$CF_RECORD_ID" \
+ -H "Authorization: Bearer $CF_API_TOKEN" \
+ -H "Content-Type: application/json" \
+ --data '{"type":"A","name":"tt.themediahub.org","content":"'"$LINODE_IP"'","ttl":60,"proxied":false}' >> $LOG_FILE 2>&1
+ echo "[$DATE] βœ… Failover to Linode triggered." >> $LOG_FILE
+else
+ echo "[$DATE] βœ… Montreal healthy. No action taken." >> $LOG_FILE
+fi