move conf templates and conf_populate to Deployment/; add rogue-prevention rules to CLAUDE.md

This commit is contained in:
Gmer4Lfe
2026-06-14 13:18:08 -04:00
parent 722e688783
commit 6153283de4
11 changed files with 38 additions and 13 deletions
+301
View File
@@ -0,0 +1,301 @@
#!/bin/bash
# ==============================================================================================
# ============================= Conf Auto-Populate =============================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Reads credentials and settings from locally running services and writes
# them into the local host conf. Safe to run multiple times — only populates
# EMPTY fields, never overwrites existing values unless --overwrite is passed.
#
# After populating, pushes the updated conf to all partners via conf_sync.sh
# so they have the fresh keys in their /tmp/.vv/ cache immediately.
#
# ==============================================================================================
# AUTO-DETECTED FIELDS
# ==============================================================================================
#
# HOSTN_OWNER from hostname (strip unRAID- prefix, lowercase)
# HOSTN_RADARR_API_KEY from Radarr config.xml (found via docker volume mount)
# HOSTN_RADARR_URL from Radarr config.xml port
# HOSTN_SONARR_API_KEY from Sonarr config.xml
# HOSTN_SONARR_URL from Sonarr config.xml port
# HOSTN_LIDARR_API_KEY from Lidarr config.xml
# HOSTN_LIDARR_URL from Lidarr config.xml port
# HOSTN_RADARR_MOVIE_ROOT from Radarr rootFolder API
# HOSTN_SONARR_TV_ROOT from Sonarr rootFolder API
# HOSTN_LIDARR_MUSIC_ROOT from Lidarr rootFolder API
# HOSTN_SABNZBD_API_KEY from sabnzbd.ini
# HOSTN_SABNZBD_URL from sabnzbd.ini port
# HOSTN_SLSKD_API_KEY from slskd config.yml
# HOSTN_SLSKD_URL from slskd config.yml port
# HOSTN_QBIT_URL from qBittorrent.conf WebUI port
# HOSTN_QBIT_USERNAME from qBittorrent.conf WebUI username
# HOSTN_QBIT_PASSWORD from qBittorrent.conf WebUI password (plaintext only)
# HOSTN_EMBY_CONTAINER fuzzy match from docker ps
# HOSTN_EMBY_URL from docker port binding
# HOSTN_JELLYFIN_CONTAINER fuzzy match from docker ps
# HOSTN_JELLYFIN_URL from docker port binding
# HOSTN_TRANSCODE_SSD from Emby/Jellyfin container /transcode volume mount
# HOSTN_SYS_WATCHDOG_NIC from ip route default gateway interface
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# conf_populate.sh Populate empty fields only
# conf_populate.sh --overwrite Overwrite all detected fields (re-sync after arr key rotation)
# conf_populate.sh --dry-run Show what would be written, no changes
# conf_populate.sh --log Verbose output
# conf_populate.sh --no-push Skip pushing to partners after update
#
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../../load_config.sh"
SCRIPTS_ROOT="$SCRIPTS_DIR"
OVERWRITE=false
NO_PUSH=false
FILTERED_ARGS=()
for arg in "$@"; do
case "$arg" in
--overwrite) OVERWRITE=true ;;
--no-push) NO_PUSH=true ;;
*) FILTERED_ARGS+=("$arg") ;;
esac
done
parse_args "${FILTERED_ARGS[@]}"
detect_hosts
[[ "$EUID" -ne 0 ]] && { error "Must be run as root"; exit 1; }
CONF_FILE="$SCRIPTS_ROOT/Configurations/${MY_ID,,}.conf"
[[ ! -f "$CONF_FILE" ]] && { error "Conf file not found: $CONF_FILE"; exit 1; }
log "$ICON_GEAR Config: conf=${CONF_FILE} overwrite=${OVERWRITE:-false} no-push=${NO_PUSH:-false}"
UPDATED=0
SKIPPED=0
echo ""
echo "━━━ $ICON_GEAR Conf Auto-Populate — $MY_ID ($LOCAL_SERVER_NAME) ━━━"
echo ""
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
[[ "$OVERWRITE" == true ]] && warn "OVERWRITE mode — existing values will be replaced"
# ── Helper: write a var into conf if empty (or --overwrite) ──────────────────
_set_conf_var() {
local var_name="$1" value="$2" label="$3"
[[ -z "$value" ]] && return
local current
current=$(grep -oP "(?<=^\s*${var_name}=\")[^\"]*" "$CONF_FILE" 2>/dev/null | head -1)
if [[ -n "$current" ]] && [[ "$OVERWRITE" == false ]]; then
log "$label: already set (${current:0:8}…) — skipping"
(( SKIPPED++ ))
return
fi
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would set $var_name = ${value:0:8}"
return
fi
if grep -q "^\s*${var_name}=" "$CONF_FILE"; then
sed -i "s|^\(\s*${var_name}\s*=\s*\)\"[^\"]*\"|\1\"${value}\"|" "$CONF_FILE"
else
printf '\n %s="%s"\n' "$var_name" "$value" >> "$CONF_FILE"
fi
info "$label: set ✅"
(( UPDATED++ ))
}
# ── Helper: find arr config dir via docker volume mount ───────────────────────
_arr_config_dir() {
local pattern="$1"
local container_name
container_name=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -im1 "^${pattern}")
[[ -z "$container_name" ]] && return 1
local config_path
config_path=$(docker inspect "$container_name" 2>/dev/null | \
jq -r '.[0].Mounts[]? | select(.Destination == "/config") | .Source' 2>/dev/null | head -1)
[[ -z "$config_path" ]] && config_path="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}/${container_name}"
[[ -d "$config_path" ]] && echo "$config_path" || return 1
}
# ── Helper: read XML tag value ────────────────────────────────────────────────
_xml_val() {
local file="$1" tag="$2"
grep -oP "(?<=<${tag}>)[^<]+" "$file" 2>/dev/null | head -1
}
# ── Helper: get host-side port for a container's internal port ────────────────
_docker_host_port() {
local container="$1" container_port="$2"
docker inspect "$container" 2>/dev/null | \
jq -r --arg p "${container_port}/tcp" \
'.[0].NetworkSettings.Ports[$p]?[0].HostPort // empty' 2>/dev/null | head -1
}
# ── Helper: get host path for a container destination mount ──────────────────
_docker_volume_host() {
local container="$1" dest="$2"
docker inspect "$container" 2>/dev/null | \
jq -r --arg d "$dest" \
'.[0].Mounts[]? | select(.Destination == $d) | .Source' 2>/dev/null | head -1
}
# ==============================================================================================
# ── Owner short name ──────────────────────────────────────────────────────────────────────────
# ==============================================================================================
owner=$(echo "$LOCAL_SERVER_NAME" | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//i' | tr '[:upper:]' '[:lower:]')
_set_conf_var "${MY_ID}_OWNER" "$owner" "Owner short name"
# ==============================================================================================
# ── Arr API keys + URLs + root paths ─────────────────────────────────────────────────────────
# ==============================================================================================
for arr in radarr sonarr lidarr; do
arr_upper="${arr^^}"
config_dir=$(_arr_config_dir "$arr") || {
log "${arr_upper}: no running container found — skipping"
continue
}
config_xml="${config_dir}/config.xml"
if [[ ! -f "$config_xml" ]]; then
log "${arr_upper}: config.xml not found at $config_xml — skipping"
continue
fi
key=$(_xml_val "$config_xml" "ApiKey")
port=$(_xml_val "$config_xml" "Port")
port="${port:-$(case $arr in radarr) echo 7878;; sonarr) echo 8989;; lidarr) echo 8686;; esac)}"
url_base="http://localhost:${port}"
_set_conf_var "${MY_ID}_${arr_upper}_API_KEY" "$key" "${arr_upper} API key"
_set_conf_var "${MY_ID}_${arr_upper}_URL" "$url_base" "${arr_upper} URL"
if [[ -n "$key" ]]; then
case "$arr" in lidarr) api_ver="v1" ;; *) api_ver="v3" ;; esac
root_json=$(curl -sf --max-time 5 \
-H "X-Api-Key: $key" "${url_base}/api/${api_ver}/rootfolder" 2>/dev/null)
root_path=$(echo "$root_json" | jq -r '.[0].path // empty' 2>/dev/null)
case "$arr" in
radarr) _set_conf_var "${MY_ID}_RADARR_MOVIE_ROOT" "$root_path" "Radarr movie root" ;;
sonarr) _set_conf_var "${MY_ID}_SONARR_TV_ROOT" "$root_path" "Sonarr TV root" ;;
lidarr) _set_conf_var "${MY_ID}_LIDARR_MUSIC_ROOT" "$root_path" "Lidarr music root" ;;
esac
fi
done
# ==============================================================================================
# ── SABnzbd API key + URL ─────────────────────────────────────────────────────────────────────
# ==============================================================================================
sab_dir=$(_arr_config_dir "sabnzbd") && {
sab_ini=$(find "$sab_dir" -maxdepth 2 -name "sabnzbd.ini" 2>/dev/null | head -1)
if [[ -f "$sab_ini" ]]; then
sab_key=$(grep -oP '(?<=^api_key\s*=\s*)\S+' "$sab_ini" 2>/dev/null | head -1)
sab_port=$(grep -oP '(?<=^port\s*=\s*)\d+' "$sab_ini" 2>/dev/null | head -1)
_set_conf_var "${MY_ID}_SABNZBD_API_KEY" "$sab_key" "SABnzbd API key"
_set_conf_var "${MY_ID}_SABNZBD_URL" "http://localhost:${sab_port:-8080}" "SABnzbd URL"
fi
}
# ==============================================================================================
# ── slskd API key + URL ───────────────────────────────────────────────────────────────────────
# ==============================================================================================
slskd_dir=$(_arr_config_dir "slskd") && {
slskd_yml=$(find "$slskd_dir" -maxdepth 2 \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null | head -1)
if [[ -f "$slskd_yml" ]]; then
slskd_key=$(grep -oP '(?<=api_key:\s)[\w-]+' "$slskd_yml" 2>/dev/null | head -1)
[[ -z "$slskd_key" ]] && \
slskd_key=$(grep -oP '(?<=apikey:\s)[\w-]+' "$slskd_yml" 2>/dev/null | head -1)
slskd_port=$(grep -oP '(?<=port:\s)\d+' "$slskd_yml" 2>/dev/null | head -1)
_set_conf_var "${MY_ID}_SLSKD_API_KEY" "$slskd_key" "slskd API key"
_set_conf_var "${MY_ID}_SLSKD_URL" "http://localhost:${slskd_port:-5030}" "slskd URL"
fi
}
# ==============================================================================================
# ── qBittorrent URL + credentials ────────────────────────────────────────────────────────────
# ==============================================================================================
qbit_dir=$(_arr_config_dir "qbittorrent") && {
qbit_conf=$(find "$qbit_dir" -maxdepth 3 -name "qBittorrent.conf" 2>/dev/null | head -1)
if [[ -f "$qbit_conf" ]]; then
qbit_port=$(grep -oP '(?<=WebUI\\Port=)\d+' "$qbit_conf" 2>/dev/null | head -1)
qbit_user=$(grep -oP '(?<=WebUI\\Username=)\S+' "$qbit_conf" 2>/dev/null | head -1)
# Only capture plaintext password — PBKDF2 hashes are not usable
qbit_pass=$(grep -oP '(?<=WebUI\\Password=)[^\r\n]+' "$qbit_conf" 2>/dev/null | \
grep -v '@ByteArray' | head -1)
_set_conf_var "${MY_ID}_QBIT_URL" "http://localhost:${qbit_port:-8080}" "qBittorrent URL"
_set_conf_var "${MY_ID}_QBIT_USERNAME" "$qbit_user" "qBittorrent username"
_set_conf_var "${MY_ID}_QBIT_PASSWORD" "$qbit_pass" "qBittorrent password"
fi
}
# ==============================================================================================
# ── Media server container names + URLs + transcode path ─────────────────────────────────────
# ==============================================================================================
transcode_dir=""
for pattern in "emby" "jellyfin"; do
container=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -im1 "^${pattern}")
[[ -z "$container" ]] && continue
case "$pattern" in
emby)
host_port=$(_docker_host_port "$container" "8096")
_set_conf_var "${MY_ID}_EMBY_CONTAINER" "$container" "Emby container name"
_set_conf_var "${MY_ID}_EMBY_URL" "http://localhost:${host_port:-8096}" "Emby URL"
;;
jellyfin)
host_port=$(_docker_host_port "$container" "8096")
_set_conf_var "${MY_ID}_JELLYFIN_CONTAINER" "$container" "Jellyfin container name"
_set_conf_var "${MY_ID}_JELLYFIN_URL" "http://localhost:${host_port:-8095}" "Jellyfin URL"
;;
esac
# Transcode path: first container with a /transcode mount wins
if [[ -z "$transcode_dir" ]]; then
transcode_dir=$(_docker_volume_host "$container" "/transcode")
fi
done
[[ -n "$transcode_dir" ]] && \
_set_conf_var "${MY_ID}_TRANSCODE_SSD" "${transcode_dir%/}/" "Transcode SSD path"
# ==============================================================================================
# ── Network interface ─────────────────────────────────────────────────────────────────────────
# ==============================================================================================
nic=$(ip route show default 2>/dev/null | grep -oP '(?<=dev )\S+' | head -1)
_set_conf_var "${MY_ID}_SYS_WATCHDOG_NIC" "$nic" "Default NIC"
# ==============================================================================================
# ── Summary + push ────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY Conf Populate Summary ━━━━━"
echo " Updated: $UPDATED field(s)"
echo " Skipped: $SKIPPED already set"
echo " Conf: $CONF_FILE"
echo "━━━━━━━━━━━━━━━━━━━━━"
if [[ "$UPDATED" -gt 0 ]] && [[ "$DRY_RUN" == false ]] && [[ "$NO_PUSH" == false ]]; then
echo ""
info "Pushing updated conf to partners..."
bash "$SCRIPTS_ROOT/System_Essentials/conf_sync.sh" --push-only "${EXTRA_FLAGS[@]}" || true
fi
+3 -3
View File
@@ -53,13 +53,13 @@
# RUNTIME MODES
# ==============================================================================================
#
# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --dry-run
# conf_upgrade.sh --template Deployment/master.conf.template --target Configurations/master.conf --dry-run
# Preview what would be added, removed, and kept — no changes written.
#
# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --backup
# conf_upgrade.sh --template Deployment/master.conf.template --target Configurations/master.conf --backup
# Apply the upgrade, writing a .bak first.
#
# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf
# conf_upgrade.sh --template Deployment/master.conf.template --target Configurations/master.conf
# Apply the upgrade in-place with no backup.
#
# ==============================================================================================
+529
View File
@@ -0,0 +1,529 @@
#!/bin/bash
# ==============================================================================================
# ========================== HOSTN CONFIGURATION — (hostname) ==================================
# ==============================================================================================
# HOSTN-specific variables — credentials, container names, share paths, failover lists.
# Sourced after master.conf — values here extend shared profile arrays and add HOSTN-specific
# identity, credentials, and container configuration.
#
# Sparse checkout (git) ensures other hosts never receive this file.
#
# DO NOT put shared config here — thresholds, toggles, profiles belong in master.conf.
# DO NOT put other hosts' variables here — they belong in their own host*.conf files.
#
# ── HOW TO USE THIS TEMPLATE ──────────────────────────────────────────────────────────────────
# This file was generated by the Varaverk first-run wizard.
# Fill in the sections that apply to your setup — leave unused sections empty.
# All scripts self-guard against empty values — safe to leave sections blank until needed.
#
# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────
#
# ── IDENTITY & CONNECTIVITY ────────────────────────────────────────────────────────────────
# IDENTITY hostname, SSH key, Unraid API key
# EMBY container name, URL, API key
# JELLYFIN container name, URL, API key
# GITEA API token for SSH key registration
# NOTIFICATIONS Discord webhook
#
# ── PARTNERSHIP ────────────────────────────────────────────────────────────────────────────
# PARTNERSHIP auth containers, backup paths, emby provisioning
#
# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────
# DAILY SYNC SHARES media shares this host owns and pushes
# PERSONAL SHARES private encrypted shares for offsite backup
# WEEKLY SYNC SHARES appdata shares synced weekly
# INTERMEDIATE SYNC mid-day appdata propagation
# CRITICAL SYNC SHARES appdata shares synced every 30 minutes
# BACKUP VERIFY shares for checksum verification against remote
# HOSTN RSYNC PROFILE host-specific appdata sync profile
#
# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────
# DDNS DDNS containers managed by this host
# INTERNET LOSS containers stopped when internet is lost
# FALLBACK TIERS what this host runs for the remote per tier
# TIER DELAYS delays before each tier activates
# RSYNC WRITEBACK appdata synced back on handback
#
# ── DOCKER ─────────────────────────────────────────────────────────────────────────────────
# DOCKER DAILY RESTART containers restarted daily
# DOCKER WEEKLY RESTART containers restarted weekly
# DOCKER WATCHDOG memory limits, health URLs, required containers
# NETWORK WATCHDOG DDNS domain, NPM URL for connectivity checks
# DOCKER NETWORK CONNECT networks and containers for array start
#
# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────
# MEDIA PERMISSIONS share list for permissions script
# MEDIA CLEANER folder lists for media_cleaner.sh
#
# ── ARR STACK ──────────────────────────────────────────────────────────────────────────────
# DOWNLOADERS slskd, SABnzbd, qBittorrent credentials and URLs
# LIDARR / SONARR / RADARR URL, API key, path map
# ARR RECOVERY per-arr recovery toggles
#
# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────
# TRANSCODES ramdisk size, thresholds, SSD path, server array
#
# ── MONITORS ───────────────────────────────────────────────────────────────────────────────
# CERTIFICATE MONITOR domains checked for SSL expiry
# SMART HEALTH drives to skip in SMART monitoring
# ZFS REPORT pools to exclude from ZFS health report
#
# ── RESOURCE MANAGER ───────────────────────────────────────────────────────────────────────
# RESOURCE MANAGER containers paused/stopped under memory pressure
#
# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────
# SYSTEM WATCHDOG per-host check toggles and NIC configuration
#
# ==============================================================================================
# ==============================================================================================
# ── STORAGE MODE ──────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ Storage mode ━━━
# Controls where Varaverk stores scripts, conf, and state files.
# true = internal NVMe/SSD — /boot/config/plugins/varaverk (write-safe, git-direct)
# false = USB flash boot — /mnt/user/appdata/Varaverk (preserves flash lifetime)
# Auto-detected from boot device transport on first setup.
# To change: Settings → Storage → Migrate.
HOSTN_STORAGE_MODE_INTERNAL=false
# ==============================================================================================
# ── IDENTITY & CONNECTIVITY ───────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ Identity ━━━
# HOSTN hostname lives in master.conf (not a credential — safe for all servers).
# SSH key used for all server-to-server operations — rsync, failover, conf sync.
# Convention: /root/.ssh/<hostname-lowercase-no-unraid-prefix>_rsync_automation
# Must be in /root/.ssh/ and authorised in the partner's /root/.ssh/authorized_keys.
# Run Partnership/ssh_setup.sh to generate the key and copy it to the partner.
HOSTN_SSH_KEY="" # e.g. /root/.ssh/myserver_rsync_automation
HOSTN_OWNER="" # short identifier for this server (e.g. myserver)
HOSTN_OWNER_EMAIL=""
# ━━━ Unraid API ━━━
# Used by the Varaverk plugin to query this server's Unraid GraphQL API.
# Generate in Unraid: Settings → Management Access → API Keys → + New Key
HOSTN_UNRAID_API_KEY=""
# ━━━ Emby ━━━
HOSTN_EMBY_CONTAINER="Emby"
HOSTN_EMBY_URL="http://localhost:8096"
HOSTN_EMBY_API_KEY="" # Emby Dashboard → API Keys → + New Key
# ━━━ Jellyfin ━━━
HOSTN_JELLYFIN_CONTAINER="Jellyfin"
HOSTN_JELLYFIN_URL="http://localhost:8095"
HOSTN_JELLYFIN_API_KEY="" # Jellyfin Dashboard → Administration → API Keys
# ━━━ Gitea ━━━
# Personal access token for gitea_ssh_setup.sh.
# Create in Gitea: Settings → Applications → Generate Token → scope: write:user
HOSTN_GITEA_API_TOKEN=""
# ━━━ Notifications ━━━
# Discord webhook — leave blank to disable.
HOSTN_DISCORD_WEBHOOK=""
# ==============================================================================================
# ── PARTNERSHIP ───────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Auth containers reconfigured on onboard/offboard.
# Format: "ContainerName|WebUIPort"
HOSTN_PARTNERSHIP_AUTH_WEBUIS=(
# "NginxProxyManager|81"
# "Authelia|9091"
)
# XML templates pushed to mirror during onboard — auth stack.
# Dependencies (databases) must come before apps that depend on them.
HOSTN_PARTNERSHIP_AUTH_STACK=(
# "my-Authelia.xml"
# "my-NginxProxyManager.xml"
)
# XML templates pushed to mirror during onboard — arr stack.
HOSTN_PARTNERSHIP_ARR_STACK=(
# "my-Sonarr.xml"
# "my-Radarr.xml"
)
# Paths the partner should collect during the grace window after offboard.
HOSTN_PARTNERSHIP_MIRROR_BACKUPS=(
# "/mnt/user/appdata-Fallback/Partner-Emby"
)
# Containers parked on this server when partnership is active.
HOSTN_PARTNERSHIP_OWN_CONTAINERS=(
# "Emby"
)
# Containers stopped on THIS server before deploying the mirror's stack on onboard.
HOSTN_PARTNERSHIP_REPLACE_CONTAINERS=(
)
# Arr containers stopped on this server when mirror's arr stack is deployed.
HOSTN_PARTNERSHIP_ARR_REPLACE_CONTAINERS=(
)
# Emby admin provisioning — owner controls whether Emby is shared.
HOSTN_PARTNERSHIP_PROVISION_EMBY_ADMIN=false
HOSTN_PARTNERSHIP_EMBY_PORT=8096
HOSTN_PARTNERSHIP_EMBY_ADMIN_USER=""
HOSTN_PARTNERSHIP_EMBY_ADMIN_PASS=""
# ==============================================================================================
# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ Daily Sync Shares ━━━
# Media shares this host pushes to all other nodes every night.
# Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed.
HOSTN_DAILY_SYNC_SHARES=(
# /mnt/user/Movies
# /mnt/user/Tv_Shows
# /mnt/user/Music
)
# ━━━ Personal Shares ━━━
# Private encrypted shares synced for offsite backup, independent of media shares.
HOSTN_PERSONAL_SHARES=(
# /mnt/user/Personal # e.g. ZFS-encrypted dataset
)
# ━━━ Weekly Sync Shares ━━━
# Appdata shares synced during the weekly maintenance window.
# Profiles (emby, critical-data) drive container stops — define in master.conf.
HOSTN_WEEKLY_SYNC_SHARES=(
# "/mnt/user/Media_Server/Emby" # emby profile
# "/mnt/user/appdata-Fallback/Critical-Data" # critical-data profile
)
# ━━━ Intermediate Sync Shares ━━━
# Shares synced every 4 hours. Leave empty to skip mid-day rsync.
HOSTN_INTERMEDIATE_SYNC_SHARES=(
# Add shares here to enable mid-day rsync
)
# ━━━ Critical Sync Shares ━━━
# Appdata shares synced every 30 minutes.
# Format: "/path/to/share" or "/path/to/share|profile-name"
HOSTN_CRITICAL_SYNC_SHARES=(
# "/mnt/user/appdata-Fallback/Critical-Data|critical-fallback"
# "/mnt/user/Media_Server/Emby|emby-fallback"
)
# ━━━ Backup Verify ━━━
# Leave empty to use HOSTN_DAILY_SYNC_SHARES automatically.
HOSTN_BACKUP_VERIFY_SHARES=(
# leave empty to use HOSTN_DAILY_SYNC_SHARES automatically
)
# ━━━ HOSTN Rsync Profile — hostn-appdata ━━━
# Host-specific appdata sync profile.
PROFILE_RSYNC_OPTS[hostn-appdata]="-av --info=progress2 --bwlimit=${PROFILE_BW_LIMIT[hostn-appdata]:-8000}"
PROFILE_BW_LIMIT[hostn-appdata]=8000
PROFILE_RETRY_COUNT[hostn-appdata]=3
PROFILE_SLEEP[hostn-appdata]=300
PROFILE_CRITICAL_CONTAINER_NAMES[hostn-appdata]=""
PROFILE_DELAYED_CONTAINERS[hostn-appdata]=""
PROFILE_CONTAINER_DELAY[hostn-appdata]=5
PROFILE_EXCLUDE_DIRS[hostn-appdata]="logs *.tmp"
# ==============================================================================================
# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ DDNS ━━━
# DDNS containers this host manages.
HOSTN_DDNS_CONTAINERS=(
# "MyServer.com"
)
# ━━━ Internet Loss ━━━
# Containers stopped immediately when internet is lost.
FALLBACK_HOSTN_STOP_ON_NO_NET=(
# "MyServer.com"
)
# ━━━ Fallback Tiers — HOSTN Runs for Partner ━━━
# Containers this host starts when the partner goes down.
# Replace REMOTE_ID below with the actual remote host ID (HOST1, HOST2, etc.)
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER1=(
# "Partner-DDNS-Container"
)
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER2=(
# "container-placeholder"
)
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER3=(
# "container-placeholder"
)
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER4=(
# "container-placeholder"
)
# ━━━ Tier Delays — This Host's Outage Timers ━━━
# How long THIS host must be down before each tier activates on the partner.
HOSTN_TIER2_DELAY=240 # 4 hours
HOSTN_TIER3_DELAY=720 # 12 hours
HOSTN_TIER4_DELAY=1440 # 24 hours
# ━━━ Rsync Writeback ━━━
HOSTN_TIER1_WRITEBACK_DELAY=60 # skip Emby writeback if outage under 1hr
FALLBACK_HOSTN_WRITEBACK_TIER1=(
# "/mnt/user/Media_Server/Emby"
)
FALLBACK_HOSTN_WRITEBACK_TIER2=(
# "/mnt/user/appdata-Fallback/Important-Data"
)
FALLBACK_HOSTN_WRITEBACK_TIER3=(
# "location-placeholder"
)
FALLBACK_HOSTN_WRITEBACK_TIER4=(
# "/mnt/user/appdata-Fallback/Arrs_Stack"
)
# ==============================================================================================
# ── DOCKER ────────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ Docker Daily Restart ━━━
HOSTN_DAILY_RESTART_CONTAINERS=(
# "NginxProxyManager"
# "Authelia"
)
# ━━━ Docker Weekly Restart ━━━
HOSTN_WEEKLY_RESTART_CONTAINERS=(
# "NextCloud"
# "AdGuard-Home"
)
# ━━━ Docker Watchdog ━━━
# Memory hard limits in MB — immediate restart if exceeded.
# 20GB=20480 16GB=16384 8GB=8192 4GB=4096 2GB=2048 1GB=1024
declare -A HOSTN_WATCHDOG_CONTAINERS=(
# ["Emby"]=18432
)
# HTTP health check URLs — checked every cycle.
declare -A HOSTN_WATCHDOG_CONTAINER_URLS=(
# ["Emby"]="http://localhost:8096"
)
# API-level health checks. Format: ["ContainerName"]="url|expected_json_key|expected_value"
declare -A HOSTN_WATCHDOG_CONTAINER_API_CHECKS=(
)
# Required containers — must always be running.
HOSTN_WATCHDOG_REQUIRED_CONTAINERS=(
# "NginxProxyManager"
# "Authelia"
)
# Containers to skip in Tier 2 global scan.
HOSTN_WATCHDOG_SCAN_IGNORE=(
# "my-occasional-container"
)
# Dependency ordering — skip restarting a container if its dependency is also down.
declare -A HOSTN_WATCHDOG_DEPENDENCIES=(
# ["Authelia"]="Mariadb Redis-Authelia"
)
# Per-container appdata growth suppress ceilings in MB.
declare -A HOSTN_WATCHDOG_APPDATA_SIZES=(
# ["Tdarr"]="25600"
)
# ━━━ Network Watchdog ━━━
HOSTN_NETWORK_WATCHDOG_DDNS_DOMAIN="" # e.g. myserver.com
HOSTN_NETWORK_WATCHDOG_DDNS_CONTAINER="" # e.g. MyServer.com
HOSTN_NETWORK_WATCHDOG_NPM_URL="" # e.g. https://myserver.com
# ━━━ Docker Network Connect ━━━
HOSTN_NETWORK_CONNECT_CONTAINERS=(
# "memcached"
)
HOSTN_NETWORK_CONNECT_NETWORKS=(
# "high-availability"
)
# ==============================================================================================
# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ Media Permissions ━━━
HOSTN_MEDIA_PERMISSION_SHARES=(
# /mnt/user/Movies
# /mnt/user/Tv_Shows
# /mnt/user/Music
# /mnt/user/Downloads
)
# ━━━ Media Cleaner ━━━
HOSTN_ANIME_CLEAN_FOLDERS=(
# /mnt/user/Anime_Movies
# /mnt/user/Anime_Shows
)
HOSTN_MEDIA_CLEAN_FOLDERS=(
# /mnt/user/Movies
# /mnt/user/Tv_Shows
)
# ==============================================================================================
# ── ARR STACK ─────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ Downloaders ━━━
HOSTN_SLSKD_URL="http://localhost:8980"
HOSTN_SLSKD_API_KEY=""
HOSTN_SLSKD_FAILED_IMPORTS_DIR=""
HOSTN_SABNZBD_URL="http://localhost:8180"
HOSTN_SABNZBD_API_KEY=""
HOSTN_QBIT_URL="http://localhost:8080"
HOSTN_QBIT_USERNAME="admin"
HOSTN_QBIT_PASSWORD=""
# ━━━ Lidarr ━━━
HOSTN_LIDARR_URL="http://localhost:8686"
HOSTN_LIDARR_API_KEY=""
HOSTN_LIDARR_MUSIC_ROOT="/mnt/user/Music"
HOSTN_FANART_API_KEY=""
HOSTN_LASTFM_API_KEY=""
declare -A HOSTN_LIDARR_PATH_MAP=(
# ["/music"]="/mnt/user/Music"
)
# ━━━ Sonarr ━━━
HOSTN_SONARR_URL="http://localhost:8989"
HOSTN_SONARR_API_KEY=""
HOSTN_SONARR_TV_ROOT="/mnt/user/Tv_Shows"
declare -A HOSTN_SONARR_PATH_MAP=(
# ["/tv"]="/mnt/user/Tv_Shows"
)
# ━━━ Radarr ━━━
HOSTN_RADARR_URL="http://localhost:7878"
HOSTN_RADARR_API_KEY=""
HOSTN_TMDB_API_KEY=""
HOSTN_RADARR_MOVIES_ROOT="/mnt/user/Movies"
declare -A HOSTN_RADARR_PATH_MAP=(
# ["/movies"]="/mnt/user/Movies"
)
# ━━━ Arr Recovery Toggles ━━━
HOSTN_LIDARR_RECOVERY=false
HOSTN_SONARR_RECOVERY=true
HOSTN_RADARR_RECOVERY=true
# ==============================================================================================
# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
HOSTN_RAMDISK_SIZE="10G"
HOSTN_RAMDISK_WARN_GB=8.5
HOSTN_RAMDISK_LOW_GB=7
HOSTN_TRANSCODE_SSD="/mnt/cache/Temp_Storage/Emby/Transcodes/"
HOSTN_TRANSCODE_SERVERS=(
"${HOSTN_EMBY_CONTAINER}|${HOSTN_EMBY_URL}|${HOSTN_EMBY_API_KEY}|emby"
)
# ==============================================================================================
# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# ━━━ Certificate Monitor ━━━
HOSTN_CERT_MONITOR_DOMAINS=(
# "myserver.com"
)
# ━━━ SMART Health ━━━
HOSTN_SMART_IGNORE_DRIVES=(
"sda" # boot USB — SMART not meaningful on flash drives
)
# ━━━ ZFS Report ━━━
HOSTN_ZFS_REPORT_IGNORE_POOLS=(
# "disk5"
)
# ==============================================================================================
# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────
# ==============================================================================================
HOSTN_RW_PAUSE_CONTAINERS=(
# "Tdarr"
# "LidaTube"
)
HOSTN_RW_STOP_CONTAINERS=(
# "Tdarr"
)
# ==============================================================================================
# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────
# ==============================================================================================
HOSTN_SYS_WATCHDOG_NIC="" # e.g. eth0 — for network monitoring
HOSTN_SYS_WATCHDOG_CHECK_DOCKER_DAEMON=true
HOSTN_SYS_WATCHDOG_CHECK_ROOTFS=true
HOSTN_SYS_WATCHDOG_CHECK_KERNEL_OOPS=true
HOSTN_SYS_WATCHDOG_CHECK_FD=true
HOSTN_SYS_WATCHDOG_CHECK_BOOT=true
HOSTN_SYS_WATCHDOG_CHECK_OOM=true
HOSTN_SYS_WATCHDOG_CHECK_RAM=true
HOSTN_SYS_WATCHDOG_CHECK_LOG=true
HOSTN_SYS_WATCHDOG_CHECK_ARC=true
HOSTN_SYS_WATCHDOG_CHECK_CPU_TEMP=true
HOSTN_SYS_WATCHDOG_CHECK_LOAD=true
HOSTN_SYS_WATCHDOG_CHECK_ZOMBIES=true
HOSTN_SYS_WATCHDOG_CHECK_CONTAINERS=true
HOSTN_SYS_WATCHDOG_CHECK_TMP=true
HOSTN_SYS_WATCHDOG_CHECK_MDSTAT=true
HOSTN_SYS_WATCHDOG_CHECK_NETWORK=true
HOSTN_SYS_WATCHDOG_CHECK_SSHD=true
HOSTN_SYS_WATCHDOG_CHECK_RUNAWAY=false
# ==============================================================================================
# ── AUTH STACK ────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Credentials for the Varaverk Auth Stack page (NPM, lldap, Authelia).
# ━━━ NginxProxyManager ━━━
# Admin API runs on 7818 (not 81 — 81 is the partnership WebUI port).
HOSTN_NPM_URL="http://localhost:7818"
HOSTN_NPM_USER="" # NPM admin email
HOSTN_NPM_PASS="" # NPM admin password
# ━━━ lldap ━━━
HOSTN_LLDAP_URL="http://localhost:17170"
HOSTN_LLDAP_USER="admin" # lldap admin username
HOSTN_LLDAP_PASS="" # lldap admin password
# ━━━ Authelia ━━━
HOSTN_AUTHELIA_CONFIG="/mnt/user/appdata/Authelia/configuration.yml"
HOSTN_AUTHELIA_CONTAINER="Authelia"
# ==============================================================================================
# ──────────────────────── End Of HOSTn Variables ──────────────────────────────────────────────
# ==============================================================================================
File diff suppressed because it is too large Load Diff