1	#!/bin/bash
2	# ==============================================================================================
3	# ================================= CONFIGURATION LOADER =======================================
4	# ==============================================================================================
5	# Single entry point for all configuration sourcing across the ecosystem.
6	# Every script sources this file instead of sourcing master.conf files directly.
7	#
8	# ── HOW IT WORKS ──────────────────────────────────────────────────────────────────────────────
9	# 1. Detects OS platform → PLATFORM=unraid|truenas|unknown; exports SCRIPTS_DIR
10	# 2. Sources master.conf (shared config — hostnames, thresholds, toggles, profiles, job lists)
11	# 3. Auto-discovers and sources all host*.conf files in the same directory
12	#    Each host conf extends the shared profile arrays and adds host-specific credentials
13	# 4. Sources common.sh (shared functions — detect_hosts, logging, notifications etc.)
14	# 5. Sources Plugin/<platform>/adapter.sh (platform_*() functions for OS-specific ops)
15	#
16	# ── WHY THIS EXISTS ───────────────────────────────────────────────────────────────────────────
17	# Without this loader every script had to explicitly source each conf file:
18	#   source master.conf
19	#   source host1.conf
20	#   source host2.conf
21	#   source common.sh
22	#
23	# Adding a new server meant updating every script.
24	# With this loader — add host3.conf to the git repo and every server
25	# auto-discovers it on next git pull. Zero script changes required. Ever.
26	#
27	# ── ADDING A NEW SERVER ───────────────────────────────────────────────────────────────────────
28	# 1. Create Configurations/host3.conf following the same structure as HOST1/HOST2
29	# 2. Commit and push to git repo
30	# 3. All servers pull it automatically — no other changes needed
31	#
32	# ── USAGE IN SCRIPTS ──────────────────────────────────────────────────────────────────────────
33	# Replace the three source lines at the top of every script with:
34	#
35	#   SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
36	#   source "$SCRIPT_DIR/../load_config.sh"
37	#
38	# Scripts in subdirectories (Rsync/, Docker_Essentials/ etc.) use ../ to reach root.
39	# Scripts in root directory use ./ instead:
40	#
41	#   SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
42	#   source "$SCRIPT_DIR/load_config.sh"
43	#
44	# ── SPARSE CHECKOUT NOTE ──────────────────────────────────────────────────────────────────────
45	# Sparse checkout controls which host*.conf files each server receives.
46	# HOST1 only pulls Configurations/host1.conf — never HOST2's credentials.
47	# HOST2 only pulls Configurations/host2.conf — never HOST1's credentials.
48	# This loader sources whatever conf files ARE present — sparse checkout handles the rest.
49	# Both servers pull all non-credential conf files (Configurations/master.conf, common.sh, load_config.sh).
50	#
51	# ==============================================================================================
52
53	# ━━━ Locate config root ━━━
54	# load_config.sh always lives in the repo root.
55	# Scripts call it from subdirectories using ../ — resolve to the actual root.
56	    LOAD_CONFIG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
57
58	# ━━━ Platform detection ━━━
59	# PLATFORM drives the adapter layer — Plugin/<platform>/adapter.sh
60	# Each platform adapter provides the same function API; scripts stay OS-agnostic.
61	    if   [[ -f /etc/unraid-version ]]; then  PLATFORM="unraid"
62	    elif [[ -f /etc/truenas ]];        then  PLATFORM="truenas"
63	    else                                     PLATFORM="unknown"
64	    fi
65	    export PLATFORM SCRIPTS_DIR="$LOAD_CONFIG_DIR"
66
67	# ━━━ Source shared config ━━━
68	# master.conf must be sourced first — it declares the shared PROFILE_* arrays
69	# that Host confs extend. Sourcing host confs before master.conf would fail.
70	    if [[ ! -f "$LOAD_CONFIG_DIR/Configurations/master.conf" ]]; then
71	        echo "[FATAL] master.conf not found at $LOAD_CONFIG_DIR/Configurations/master.conf" >&2
72	        echo "[FATAL] Check TARGET_DIR and git pull status" >&2
73	        exit 1
74	    fi
75	    source "$LOAD_CONFIG_DIR/Configurations/master.conf"
76
77	# ━━━ Auto-discover and source all host*.conf files ━━━
78	# Sorted for consistent load order — HOST1 before HOST2 before HOST3 etc.
79	# Each host conf extends the shared PROFILE_* arrays and adds host-specific vars.
80	# Missing files are silently skipped — sparse checkout intentionally withholds some.
81	# At least one host conf must be present or the ecosystem has no identity to work with.
82	    _host_confs_loaded=0
83	    declare -A _disk_conf_basenames=()
84
85	    # Use a sorted array glob — avoids word-splitting on paths with spaces
86	    while IFS= read -r _conf; do
87	        [[ -f "$_conf" ]] || continue
88	        source "$_conf"
89	        (( _host_confs_loaded++ ))
90	        _disk_conf_basenames["$(basename "$_conf")"]=1
91	        [[ "${ENABLE_LOGGING:-false}" == "true" ]] && \
92	            echo "[LOG] Loaded host config: $(basename "$_conf")" >&2
93	    done < <(printf '%s\n' "$LOAD_CONFIG_DIR/Configurations"/host*.conf 2>/dev/null | sort)
94
95	    if [[ "$_host_confs_loaded" -eq 0 ]]; then
96	        echo "[FATAL] No host*.conf files found in $LOAD_CONFIG_DIR" >&2
97	        echo "[FATAL] At least one host conf required — check git pull and sparse checkout" >&2
98	        exit 1
99	    fi
100
101	# ━━━ Source partner confs from /tmp cache ━━━
102	# conf_sync.sh pulls partner host*.conf files into /tmp/.vv/config/cached/.confs/
103	# on array start and after any conf save. Sourcing them here makes partner vars
104	# (HOST2_*, HOST3_*, …) available without committing credentials to the git repo
105	# or violating sparse checkout — partner confs live in RAM only, cleared on reboot.
106	# Confs already loaded from disk are skipped — disk copy is authoritative.
107	    _VV_CONF_CACHE="/tmp/.vv/config/cached/.confs"
108	    if [[ -d "$_VV_CONF_CACHE" ]]; then
109	        while IFS= read -r _conf; do
110	            [[ -f "$_conf" ]] || continue
111	            _conf_base="$(basename "$_conf")"
112	            # Skip if already sourced from disk
113	            [[ -n "${_disk_conf_basenames[$_conf_base]:-}" ]] && continue
114	            source "$_conf"
115	            [[ "${ENABLE_LOGGING:-false}" == "true" ]] && \
116	                echo "[LOG] Loaded cached partner config: $_conf_base" >&2
117	        done < <(printf '%s\n' "$_VV_CONF_CACHE"/host*.conf 2>/dev/null | sort)
118	    fi
119	    unset _VV_CONF_CACHE _conf_base _disk_conf_basenames
120
121	# ━━━ Source shared functions ━━━
122	# common.sh sourced last — it calls detect_hosts() which needs HOST* vars to be set.
123	    if [[ ! -f "$LOAD_CONFIG_DIR/common.sh" ]]; then
124	        echo "[FATAL] common.sh not found at $LOAD_CONFIG_DIR/common.sh" >&2
125	        exit 1
126	    fi
127	    source "$LOAD_CONFIG_DIR/common.sh"
128
129	# ━━━ Source platform adapter ━━━
130	# Provides platform_*() functions used by common.sh and scripts.
131	# Guard lets the ecosystem run before Plugin/<platform>/adapter.sh exists.
132	    _adapter="$LOAD_CONFIG_DIR/Plugin/$PLATFORM/adapter.sh"
133	    [[ -f "$_adapter" ]] && source "$_adapter"
134
135	# ━━━ Derived path constants ━━━
136	# Centralised here so every script that sources load_config.sh has them without
137	# re-deriving from SCRIPTS_DIR or hardcoding /var/log or /tmp paths inline.
138	    CONF_DIR="${SCRIPTS_DIR}/Configurations"
139	    LOG_DIR="/var/log/varaverk"
140	    VV_CACHE_DIR="/tmp/vv_cache"
141	    export CONF_DIR LOG_DIR VV_CACHE_DIR
142
143	# ━━━ Cleanup ━━━
144	    unset _conf _host_confs_loaded _adapter LOAD_CONFIG_DIR

#!/bin/bash
# ==============================================================================================
# ================================= CONFIGURATION LOADER =======================================
# ==============================================================================================
# Single entry point for all configuration sourcing across the ecosystem.
# Every script sources this file instead of sourcing master.conf files directly.
#
# ── HOW IT WORKS ──────────────────────────────────────────────────────────────────────────────
# 1. Detects OS platform → PLATFORM=unraid|truenas|unknown; exports SCRIPTS_DIR
# 2. Sources master.conf (shared config — hostnames, thresholds, toggles, profiles, job lists)
# 3. Auto-discovers and sources all host*.conf files in the same directory
#    Each host conf extends the shared profile arrays and adds host-specific credentials
# 4. Sources common.sh (shared functions — detect_hosts, logging, notifications etc.)
# 5. Sources Plugin/<platform>/adapter.sh (platform_*() functions for OS-specific ops)
#
# ── WHY THIS EXISTS ───────────────────────────────────────────────────────────────────────────
# Without this loader every script had to explicitly source each conf file:
#   source master.conf
#   source host1.conf
#   source host2.conf
#   source common.sh
#
# Adding a new server meant updating every script.
# With this loader — add host3.conf to the git repo and every server
# auto-discovers it on next git pull. Zero script changes required. Ever.
#
# ── ADDING A NEW SERVER ───────────────────────────────────────────────────────────────────────
# 1. Create Configurations/host3.conf following the same structure as HOST1/HOST2
# 2. Commit and push to git repo
# 3. All servers pull it automatically — no other changes needed
#
# ── USAGE IN SCRIPTS ──────────────────────────────────────────────────────────────────────────
# Replace the three source lines at the top of every script with:
#
#   SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
#   source "$SCRIPT_DIR/../load_config.sh"
#
# Scripts in subdirectories (Rsync/, Docker_Essentials/ etc.) use ../ to reach root.
# Scripts in root directory use ./ instead:
#
#   SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
#   source "$SCRIPT_DIR/load_config.sh"
#
# ── SPARSE CHECKOUT NOTE ──────────────────────────────────────────────────────────────────────
# Sparse checkout controls which host*.conf files each server receives.
# HOST1 only pulls Configurations/host1.conf — never HOST2's credentials.
# HOST2 only pulls Configurations/host2.conf — never HOST1's credentials.
# This loader sources whatever conf files ARE present — sparse checkout handles the rest.
# Both servers pull all non-credential conf files (Configurations/master.conf, common.sh, load_config.sh).
#
# ==============================================================================================

# ━━━ Locate config root ━━━
# load_config.sh always lives in the repo root.
# Scripts call it from subdirectories using ../ — resolve to the actual root.
    LOAD_CONFIG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# ━━━ Platform detection ━━━
# PLATFORM drives the adapter layer — Plugin/<platform>/adapter.sh
# Each platform adapter provides the same function API; scripts stay OS-agnostic.
    if   [[ -f /etc/unraid-version ]]; then  PLATFORM="unraid"
    elif [[ -f /etc/truenas ]];        then  PLATFORM="truenas"
    else                                     PLATFORM="unknown"
    fi
    export PLATFORM SCRIPTS_DIR="$LOAD_CONFIG_DIR"

# ━━━ Source shared config ━━━
# master.conf must be sourced first — it declares the shared PROFILE_* arrays
# that Host confs extend. Sourcing host confs before master.conf would fail.
    if [[ ! -f "$LOAD_CONFIG_DIR/Configurations/master.conf" ]]; then
        echo "[FATAL] master.conf not found at $LOAD_CONFIG_DIR/Configurations/master.conf" >&2
        echo "[FATAL] Check TARGET_DIR and git pull status" >&2
        exit 1
    fi
    source "$LOAD_CONFIG_DIR/Configurations/master.conf"

# ━━━ Auto-discover and source all host*.conf files ━━━
# Sorted for consistent load order — HOST1 before HOST2 before HOST3 etc.
# Each host conf extends the shared PROFILE_* arrays and adds host-specific vars.
# Missing files are silently skipped — sparse checkout intentionally withholds some.
# At least one host conf must be present or the ecosystem has no identity to work with.
    _host_confs_loaded=0
    declare -A _disk_conf_basenames=()

    # Use a sorted array glob — avoids word-splitting on paths with spaces
    while IFS= read -r _conf; do
        [[ -f "$_conf" ]] || continue
        source "$_conf"
        (( _host_confs_loaded++ ))
        _disk_conf_basenames["$(basename "$_conf")"]=1
        [[ "${ENABLE_LOGGING:-false}" == "true" ]] && \
            echo "[LOG] Loaded host config: $(basename "$_conf")" >&2
    done < <(printf '%s\n' "$LOAD_CONFIG_DIR/Configurations"/host*.conf 2>/dev/null | sort)

    if [[ "$_host_confs_loaded" -eq 0 ]]; then
        echo "[FATAL] No host*.conf files found in $LOAD_CONFIG_DIR" >&2
        echo "[FATAL] At least one host conf required — check git pull and sparse checkout" >&2
        exit 1
    fi

# ━━━ Source partner confs from /tmp cache ━━━
# conf_sync.sh pulls partner host*.conf files into /tmp/.vv/config/cached/.confs/
# on array start and after any conf save. Sourcing them here makes partner vars
# (HOST2_*, HOST3_*, …) available without committing credentials to the git repo
# or violating sparse checkout — partner confs live in RAM only, cleared on reboot.
# Confs already loaded from disk are skipped — disk copy is authoritative.
    _VV_CONF_CACHE="/tmp/.vv/config/cached/.confs"
    if [[ -d "$_VV_CONF_CACHE" ]]; then
        while IFS= read -r _conf; do
            [[ -f "$_conf" ]] || continue
            _conf_base="$(basename "$_conf")"
            # Skip if already sourced from disk
            [[ -n "${_disk_conf_basenames[$_conf_base]:-}" ]] && continue
            source "$_conf"
            [[ "${ENABLE_LOGGING:-false}" == "true" ]] && \
                echo "[LOG] Loaded cached partner config: $_conf_base" >&2
        done < <(printf '%s\n' "$_VV_CONF_CACHE"/host*.conf 2>/dev/null | sort)
    fi
    unset _VV_CONF_CACHE _conf_base _disk_conf_basenames

# ━━━ Source shared functions ━━━
# common.sh sourced last — it calls detect_hosts() which needs HOST* vars to be set.
    if [[ ! -f "$LOAD_CONFIG_DIR/common.sh" ]]; then
        echo "[FATAL] common.sh not found at $LOAD_CONFIG_DIR/common.sh" >&2
        exit 1
    fi
    source "$LOAD_CONFIG_DIR/common.sh"

# ━━━ Source platform adapter ━━━
# Provides platform_*() functions used by common.sh and scripts.
# Guard lets the ecosystem run before Plugin/<platform>/adapter.sh exists.
    _adapter="$LOAD_CONFIG_DIR/Plugin/$PLATFORM/adapter.sh"
    [[ -f "$_adapter" ]] && source "$_adapter"

# ━━━ Derived path constants ━━━
# Centralised here so every script that sources load_config.sh has them without
# re-deriving from SCRIPTS_DIR or hardcoding /var/log or /tmp paths inline.
    CONF_DIR="${SCRIPTS_DIR}/Configurations"
    LOG_DIR="/var/log/varaverk"
    VV_CACHE_DIR="/tmp/vv_cache"
    export CONF_DIR LOG_DIR VV_CACHE_DIR

# ━━━ Cleanup ━━━
    unset _conf _host_confs_loaded _adapter LOAD_CONFIG_DIR


Bash

grep -rn "\${STATE_DIR:-\|\${DATA_DIR:-\|\${LOG_DIR:-\|\${VARAVERK_SETUP_FILE:-\|/boot/config\b\|/var/local/emhttp\b\|/mnt/user\b" \
    /boot/config/plugins/varaverk/ \
    --include="*.sh" \
    2>/dev/null | grep -v "^Binary" | grep -v "/Plugin/unraid/" | grep -v "gitea" | grep -v "^.*:#" \
    | grep -v "\.md:" | sort | head -60

Find all hardcoded path defaults in core scripts


/boot/config/plugins/varaverk/Deployment/deploy.sh:17:PROD_ROOT="/boot/config/plugins/varaverk"
/boot/config/plugins/varaverk/Fallback/fallback.sh:348:    local state_file="${STATE_DIR:-/boot/config}/partnership_${LOCAL_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Media/arr_sync.sh:181:DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}"
/boot/config/plugins/varaverk/Orchestrators/watchdog_orchestrator.sh:128:log "$ICON_DISK Array: /mnt/user mounted (shfs) ✅"
/boot/config/plugins/varaverk/Partnership/onboard_cancel.sh:57:STATE_FILE="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
/boot/config/plugins/varaverk/Partnership/onboard_cancel.sh:91:                 sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" /boot/config/varaverk_setup.db 2>/dev/null
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1183:        local_state_file="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:197:BLOCKLIST_FILE="${PARTNERSHIP_BLOCKLIST_FILE:-${STATE_DIR:-/boot/config}/partnership_blocklist.db}"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:270:LOCAL_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${LOCAL_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:271:REMOTE_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${REMOTE_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:272:OWNER_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${OWNER}.db"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:273:MIRROR_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${MIRROR}.db"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:274:OFFLINE_COUNTER="${STATE_DIR:-/boot/config}/partnership_offline_days.db"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:877:    mountpoint -q /mnt/user 2>/dev/null || { error "Local array not healthy"; return 1; }
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:885:        "mountpoint -q /mnt/user && timeout 10 docker ps" >/dev/null 2>&1 || {
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:909:                "/mnt/user/appdata-Fallback/Critical-Data" \
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:912:                "/mnt/user/Media_Server/Emby" \
/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh:123:LOCAL_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${LOCAL_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh:124:REMOTE_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${REMOTE_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh:125:OWNER_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${OWNER}.db"
/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh:126:MIRROR_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${MIRROR}.db"
/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh:127:OFFLINE_COUNTER="${STATE_DIR:-/boot/config}/partnership_offline_days.db"
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:223:    local state_file="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:315:            'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'" 2>/dev/null' 2>/dev/null | tr -d '[:space:]')
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:316:        OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:414:                    local _setup_f="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:143:LOCAL_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${LOCAL_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:144:REMOTE_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${REMOTE_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:291:        "grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" \
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:293:    _REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}"
/boot/config/plugins/varaverk/Plugin/build.sh:107:        /boot/config/plugins/$PLUGIN_NAME/ and the .plg uses the cached copy)
/boot/config/plugins/varaverk/Rsync/rsync.sh:183:BLOCKLIST_FILE="${PARTNERSHIP_BLOCKLIST_FILE:-${STATE_DIR:-/boot/config}/partnership_blocklist.db}"
/boot/config/plugins/varaverk/System_Essentials/conf_sync.sh:103:        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:126:CLAUDE_MD="/boot/config/plugins/varaverk/CLAUDE.md"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:54:CONF_DIR="/boot/config/plugins/varaverk/Configurations"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:65:    CLAUDE_DATA="/boot/config/plugins/varaverk/claude-data"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:66:    CLAUDE_BIN="/boot/config/plugins/varaverk/claude-bin"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:69:    PERSIST_DIR="/mnt/user/appdata/claude-code"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:77:    if ! mountpoint -q /mnt/user 2>/dev/null; then
/boot/config/plugins/varaverk/Tools/claude_startup.sh:78:        _err "Array not mounted — /mnt/user not available (required for appdata mode)"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:87:if [[ "$STORAGE_INTERNAL" == true && -d /boot/config/claude && "$CLAUDE_DATA" != "/boot/config/claude" ]]; then
/boot/config/plugins/varaverk/Tools/claude_startup.sh:89:        _warn "Migrating old /boot/config/claude → $CLAUDE_DATA"
/boot/config/plugins/varaverk/Tools/claude_startup.sh:90:        cp -a /boot/config/claude/. "$CLAUDE_DATA/"
/boot/config/plugins/varaverk/Tools/container_data_export.sh:103:    error "Example: container_data_export.sh Emby /mnt/media-servers/Media_Server/Emby /mnt/user/Backups/"
/boot/config/plugins/varaverk/common.sh:1000:    # Tier 3 — shfs fallback via /mnt/user
/boot/config/plugins/varaverk/common.sh:1004:            "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
/boot/config/plugins/varaverk/common.sh:1019:                    "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
/boot/config/plugins/varaverk/common.sh:735:    [[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]]
/boot/config/plugins/varaverk/common.sh:739:    [[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]]
/boot/config/plugins/varaverk/common.sh:751:    if ! mountpoint -q /mnt/user 2>/dev/null; then
/boot/config/plugins/varaverk/common.sh:752:        error "$ICON_DISK Local array is not started — /mnt/user is not mounted"
/boot/config/plugins/varaverk/common.sh:756:    file_count=$(ls /mnt/user 2>/dev/null | wc -l)
/boot/config/plugins/varaverk/common.sh:776:        "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
/boot/config/plugins/varaverk/git_pull_execute.sh:258:    _BOOT_DIR="/boot/config/plugins/varaverk"


lets do it.... ive gotton close to the cor scipts being universal, then app layer and adpaters make everything work.. long term goal is running on other distros, and being able be cross comunicate tru…


Monitor media server sync and backup health


Let me read the specific functions in `common.sh` that still have hardcoded paths:


728	# ==============================================================================================
729	# ── UNRAID SERVICE STATE ──────────────────────────────────────────────────────────────────────
730	# ==============================================================================================
731	# Reads Unraid config files to check whether Docker and VM Manager are enabled.
732	# Use these guards before any script that manages containers or VMs.
733
734	is_docker_enabled() {
735	    [[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]]
736	}
737
738	is_vm_manager_enabled() {
739	    [[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]]
740	}
741
742	# ==============================================================================================
743	# ── LOCAL HEALTH CHECKS ───────────────────────────────────────────────────────────────────────
744	# ==============================================================================================
745
746	# Verifies local /mnt/user is mounted and has shares.
747	# Non-fatal — returns status for caller to decide.
748	# Used by fallback before starting remote containers locally.
749	check_local_array() {
750	    log "Checking local array..."
751	    if ! mountpoint -q /mnt/user 2>/dev/null; then
752	        error "$ICON_DISK Local array is not started — /mnt/user is not mounted"
753	        return 1
754	    fi
755	    local file_count
756	    file_count=$(ls /mnt/user 2>/dev/null | wc -l)
757	    if [[ "$file_count" -eq 0 ]]; then
758	        error "$ICON_DISK Local array appears empty — shares may not be available"
759	        return 1
760	    fi
761	    log "Local array is healthy"
762	    return 0
763	}
764
765	# ==============================================================================================
766	# ── REMOTE HEALTH CHECKS ─────────────────────────────────────────────────────────────────────
767	# ==============================================================================================
768
769	# Verifies remote /mnt/user is mounted via SSH.
770	# Non-fatal — returns status. Used before handback rsync.
771	# Syncing to remote with no array fills rootfs rapidly.
772	check_remote_array() {
773	    log "Checking remote array on $REMOTE_SERVER_NAME..."
774	    local result
775	    result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
776	        "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
777	    if [[ "$result" != "yes" ]]; then
778	        error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME"
779	        return 1
780	    fi
781	    log "Remote array is healthy"
782	    return 0
783	}
784
785	# Verifies remote Docker daemon is responding.
786	# Non-fatal — returns status. A hung daemon means container commands silently fail.
787	check_remote_docker() {

# ==============================================================================================
# ── UNRAID SERVICE STATE ──────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Reads Unraid config files to check whether Docker and VM Manager are enabled.
# Use these guards before any script that manages containers or VMs.

is_docker_enabled() {
    [[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]]
}

is_vm_manager_enabled() {
    [[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]]
}

# ==============================================================================================
# ── LOCAL HEALTH CHECKS ───────────────────────────────────────────────────────────────────────
# ==============================================================================================

# Verifies local /mnt/user is mounted and has shares.
# Non-fatal — returns status for caller to decide.
# Used by fallback before starting remote containers locally.
check_local_array() {
    log "Checking local array..."
    if ! mountpoint -q /mnt/user 2>/dev/null; then
        error "$ICON_DISK Local array is not started — /mnt/user is not mounted"
        return 1
    fi
    local file_count
    file_count=$(ls /mnt/user 2>/dev/null | wc -l)
    if [[ "$file_count" -eq 0 ]]; then
        error "$ICON_DISK Local array appears empty — shares may not be available"
        return 1
    fi
    log "Local array is healthy"
    return 0
}

# ==============================================================================================
# ── REMOTE HEALTH CHECKS ─────────────────────────────────────────────────────────────────────
# ==============================================================================================

# Verifies remote /mnt/user is mounted via SSH.
# Non-fatal — returns status. Used before handback rsync.
# Syncing to remote with no array fills rootfs rapidly.
check_remote_array() {
    log "Checking remote array on $REMOTE_SERVER_NAME..."
    local result
    result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
        "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
    if [[ "$result" != "yes" ]]; then
        error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME"
        return 1
    fi
    log "Remote array is healthy"
    return 0
}

# Verifies remote Docker daemon is responding.
# Non-fatal — returns status. A hung daemon means container commands silently fail.
check_remote_docker() {


870	        log "Emby admin '$username' removed ✅"
871	    else
872	        warn "Failed to delete Emby user '$username' (HTTP $del_code) — remove manually"
873	    fi
874	}
875
876	check_both_healthy() {
877	    mountpoint -q /mnt/user 2>/dev/null || { error "Local array not healthy"; return 1; }
878
879	    local mirror_ip
880	    mirror_ip=$(resolve_tailscale_ip "$MIRROR")
881	    [[ -z "$mirror_ip" ]] && { error "Cannot resolve $MIRROR Tailscale IP"; return 1; }
882
883	    timeout "$SSH_TIMEOUT" ssh -i "$MIRROR_SSH_KEY" \
884	        -o ConnectTimeout="$SSH_TIMEOUT" root@"$mirror_ip" \
885	        "mountpoint -q /mnt/user && timeout 10 docker ps" >/dev/null 2>&1 || {
886	        error "Mirror $MIRROR not healthy"
887	        return 1
888	    }
889	    return 0
890	}
891
892	do_final_sync() {
893	    log "Running final critical sync..."
894	    if [[ "$DRY_RUN" == false ]]; then
895	        if [[ "${#CRITICAL_SYNC_SHARES[@]}" -gt 0 ]]; then
896	            for _share in "${CRITICAL_SYNC_SHARES[@]}"; do
897	                [[ -z "$_share" ]] && continue
898	                local _path="${_share%%|*}"
899	                local _profile="${_share##*|}"
900	                if [[ "$_path" == "$_profile" ]]; then
901	                    bash "$SCRIPT_DIR/../Rsync/rsync.sh" "$_path" --log
902	                else
903	                    bash "$SCRIPT_DIR/../Rsync/rsync.sh" "$_path" \
904	                        --profile="$_profile" --log
905	                fi
906	            done
907	        else
908	            bash "$SCRIPT_DIR/../Rsync/rsync.sh" \
909	                "/mnt/user/appdata-Fallback/Critical-Data" \
910	                --profile=critical-fallback --log
911	            bash "$SCRIPT_DIR/../Rsync/rsync.sh" \
912	                "/mnt/user/Media_Server/Emby" \
913	                --profile=emby-fallback --log
914	        fi
915	    else
916	        warn "DRY RUN — would run final critical sync (${#CRITICAL_SYNC_SHARES[@]:-hardcoded} shares)"
917	    fi
918	    warn "Final sync complete — mirror has current state ✅"
919	}

        log "Emby admin '$username' removed ✅"
    else
        warn "Failed to delete Emby user '$username' (HTTP $del_code) — remove manually"
    fi
}

check_both_healthy() {
    mountpoint -q /mnt/user 2>/dev/null || { error "Local array not healthy"; return 1; }

    local mirror_ip
    mirror_ip=$(resolve_tailscale_ip "$MIRROR")
    [[ -z "$mirror_ip" ]] && { error "Cannot resolve $MIRROR Tailscale IP"; return 1; }

    timeout "$SSH_TIMEOUT" ssh -i "$MIRROR_SSH_KEY" \
        -o ConnectTimeout="$SSH_TIMEOUT" root@"$mirror_ip" \
        "mountpoint -q /mnt/user && timeout 10 docker ps" >/dev/null 2>&1 || {
        error "Mirror $MIRROR not healthy"
        return 1
    }
    return 0
}

do_final_sync() {
    log "Running final critical sync..."
    if [[ "$DRY_RUN" == false ]]; then
        if [[ "${#CRITICAL_SYNC_SHARES[@]}" -gt 0 ]]; then
            for _share in "${CRITICAL_SYNC_SHARES[@]}"; do
                [[ -z "$_share" ]] && continue
                local _path="${_share%%|*}"
                local _profile="${_share##*|}"
                if [[ "$_path" == "$_profile" ]]; then
                    bash "$SCRIPT_DIR/../Rsync/rsync.sh" "$_path" --log
                else
                    bash "$SCRIPT_DIR/../Rsync/rsync.sh" "$_path" \
                        --profile="$_profile" --log
                fi
            done
        else
            bash "$SCRIPT_DIR/../Rsync/rsync.sh" \
                "/mnt/user/appdata-Fallback/Critical-Data" \
                --profile=critical-fallback --log
            bash "$SCRIPT_DIR/../Rsync/rsync.sh" \
                "/mnt/user/Media_Server/Emby" \
                --profile=emby-fallback --log
        fi
    else
        warn "DRY RUN — would run final critical sync (${#CRITICAL_SYNC_SHARES[@]:-hardcoded} shares)"
    fi
    warn "Final sync complete — mirror has current state ✅"
}


Let me also read the specific contexts for the remaining hardcoded paths before starting:


grep -n "STATE_DIR\|DOCKER_APPDATA" /boot/config/plugins/varaverk/Configurations/master.conf | head -10

Check if STATE_DIR is set in master.conf


122:#   STATE_DIR  — runtime state files for all scripts (watchdogs, fallback, transcode, etc.)
123:#                Requirement: ALL state files MUST use $STATE_DIR. No /tmp, no /boot/config root.
125:    STATE_DIR="/boot/config/plugins/varaverk/State_Files"
144:    DOCKER_APPDATA_BASE="/mnt/user/appdata"
164:# State files in STATE_DIR — survives reboots, array must be up (scripts run after array start):
165:#   STATE_DIR/partnership_HOST1.db ← HOST1 writes only
166:#   STATE_DIR/partnership_HOST2.db ← HOST2 writes only
167:#   STATE_DIR/partnership_blocklist.db ← shared blocklist
168:#   STATE_DIR/varaverk_setup.db ← onboarding progress state
199:# Partnership and setup state files — all in STATE_DIR per the project requirement.


sed -n '85p;88p;91p;92p' /boot/config/plugins/varaverk/Partnership/onboard_cancel.sh
sed -n '100p;103p' /boot/config/plugins/varaverk/System_Essentials/conf_sync.sh
sed -n '255p;258p;260p' /boot/config/plugins/varaverk/git_pull_execute.sh
sed -n '125p;128p;130p' /boot/config/plugins/varaverk/Orchestrators/watchdog_orchestrator.sh
sed -n '310p;315p;316p;318p' /boot/config/plugins/varaverk/Partnership/partnership_onboard.sh
sed -n '288p;291p;293p;295p' /boot/config/plugins/varaverk/Partnership/partnership_transfer.sh

Read context around each hardcoded path in core scripts


            warn "DRY RUN — would remove HOST1 key from $MIRROR:/root/.ssh/authorized_keys"
            timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
                 sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" /boot/config/varaverk_setup.db 2>/dev/null
                 echo ok" 2>/dev/null | grep -q ok && {

        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
    # ── Flash mode: sync Plugin/ to /boot/ so the webUI picks up updates ─────
    _BOOT_DIR="/boot/config/plugins/varaverk"
        echo ""
    echo "Array not started — skipping watchdog cycle"
log "$ICON_DISK Array: /mnt/user mounted (shfs) ✅"
# ==============================================================================================

            'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'" 2>/dev/null' 2>/dev/null | tr -d '[:space:]')
        OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"
        if [[ "$DRY_RUN" == true ]]; then
    # master.conf is shared — host-specific credentials live in host*.conf.
        "grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" \
    _REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}"
        -o ConnectTimeout="$SSH_TIMEOUT" \


Critical insight from master.conf: `STATE_DIR` is already set there, and it's `"/boot/config/plugins/varaverk/State_Files"`. Since `SCRIPTS_DIR` is set before master.conf loads (in `load_config.sh` line 65), the fix is just making it relative. Let me read the exact context:


118	# scripts, state, and data are all available before the array mounts.
119	# Both directories are created automatically if they don't exist.
120	#
121	#   DATA_DIR   — historical logs, statistics, discovery histories, blocklists
122	#   STATE_DIR  — runtime state files for all scripts (watchdogs, fallback, transcode, etc.)
123	#                Requirement: ALL state files MUST use $STATE_DIR. No /tmp, no /boot/config root.
124	    DATA_DIR="/boot/config/plugins/varaverk/data"
125	    STATE_DIR="/boot/config/plugins/varaverk/State_Files"
126
127	# ── Version Parity ──
128	# Controls behaviour when local and remote unRAID versions differ.
129	# Major version mismatch always aborts regardless of this setting.
130	# "warn"  — log warning and continue (safe for minor/patch differences)
131	# "abort" — refuse to continue (strict — ensures both sides always match)
132	    UNRAID_VERSION_MISMATCH_ACTION="warn"
133
134	# ── Arr Sync ──
135	# arr_sync.sh syncs Lidarr/Sonarr/Radarr libraries across all nodes bidirectionally.
136	# Runs before rsync — all nodes agree on tracked library before files are transferred.
137	# Remote API keys are read live from each node's config.xml via SSH — never stored here.
138	    CONF_SYNC_ENABLED=false             # HOST2 rebuild in progress — re-enable when HOST2 is back online
139
140	    ARR_SYNC_ENABLED=true
141	    ARR_SYNC_BLOCKLIST="${DATA_DIR}/arr_sync_blocklist.tsv"
142	    ARR_SYNC_CONNECT_TIMEOUT=10     # seconds — SSH connect timeout per node
143	    ARR_SYNC_API_TIMEOUT=60         # seconds — curl timeout for library fetches
144	    DOCKER_APPDATA_BASE="/mnt/user/appdata"
145	    ARR_SYNC_LIDARR_PORT=8686
146	    ARR_SYNC_SONARR_PORT=8989
147	    ARR_SYNC_RADARR_PORT=7878
148
149	# ── Remote Docker Daemon ──
150	# Strike system for remote Docker daemon health checks.
151	# Scripts that issue remote container commands check the remote daemon first.
152	# Below limit → skip operation this run | At limit → notify critical + exit

# scripts, state, and data are all available before the array mounts.
# Both directories are created automatically if they don't exist.
#
#   DATA_DIR   — historical logs, statistics, discovery histories, blocklists
#   STATE_DIR  — runtime state files for all scripts (watchdogs, fallback, transcode, etc.)
#                Requirement: ALL state files MUST use $STATE_DIR. No /tmp, no /boot/config root.
    DATA_DIR="/boot/config/plugins/varaverk/data"
    STATE_DIR="/boot/config/plugins/varaverk/State_Files"

# ── Version Parity ──
# Controls behaviour when local and remote unRAID versions differ.
# Major version mismatch always aborts regardless of this setting.
# "warn"  — log warning and continue (safe for minor/patch differences)
# "abort" — refuse to continue (strict — ensures both sides always match)
    UNRAID_VERSION_MISMATCH_ACTION="warn"

# ── Arr Sync ──
# arr_sync.sh syncs Lidarr/Sonarr/Radarr libraries across all nodes bidirectionally.
# Runs before rsync — all nodes agree on tracked library before files are transferred.
# Remote API keys are read live from each node's config.xml via SSH — never stored here.
    CONF_SYNC_ENABLED=false             # HOST2 rebuild in progress — re-enable when HOST2 is back online

    ARR_SYNC_ENABLED=true
    ARR_SYNC_BLOCKLIST="${DATA_DIR}/arr_sync_blocklist.tsv"
    ARR_SYNC_CONNECT_TIMEOUT=10     # seconds — SSH connect timeout per node
    ARR_SYNC_API_TIMEOUT=60         # seconds — curl timeout for library fetches
    DOCKER_APPDATA_BASE="/mnt/user/appdata"
    ARR_SYNC_LIDARR_PORT=8686
    ARR_SYNC_SONARR_PORT=8989
    ARR_SYNC_RADARR_PORT=7878

# ── Remote Docker Daemon ──
# Strike system for remote Docker daemon health checks.
# Scripts that issue remote container commands check the remote daemon first.
# Below limit → skip operation this run | At limit → notify critical + exit


140	    ARR_SYNC_ENABLED=true
141	    ARR_SYNC_BLOCKLIST="${DATA_DIR}/arr_sync_blocklist.tsv"
142	    ARR_SYNC_CONNECT_TIMEOUT=10     # seconds — SSH connect timeout per node
143	    ARR_SYNC_API_TIMEOUT=60         # seconds — curl timeout for library fetches
144	    DOCKER_APPDATA_BASE="/mnt/user/appdata"
145	    ARR_SYNC_LIDARR_PORT=8686
146	    ARR_SYNC_SONARR_PORT=8989
147	    ARR_SYNC_RADARR_PORT=7878
148
149	# ── Remote Docker Daemon ──

    ARR_SYNC_ENABLED=true
    ARR_SYNC_BLOCKLIST="${DATA_DIR}/arr_sync_blocklist.tsv"
    ARR_SYNC_CONNECT_TIMEOUT=10     # seconds — SSH connect timeout per node
    ARR_SYNC_API_TIMEOUT=60         # seconds — curl timeout for library fetches
    DOCKER_APPDATA_BASE="/mnt/user/appdata"
    ARR_SYNC_LIDARR_PORT=8686
    ARR_SYNC_SONARR_PORT=8989
    ARR_SYNC_RADARR_PORT=7878

# ── Remote Docker Daemon ──


54	MIRROR_ID=$( [[ "$OWNER_ID" == "HOST1" ]] && echo "HOST2" || echo "HOST1" )
55	MIRROR="${!MIRROR_ID}"
56	SSH_KEY_PUB="${SSH_KEY}.pub"
57	STATE_FILE="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
58	AUTH_KEYS="/root/.ssh/authorized_keys"
59	MIRROR_SHORT="${MIRROR%%.*}"
60
61	START=$(date +%s)
62
63	echo ""

MIRROR_ID=$( [[ "$OWNER_ID" == "HOST1" ]] && echo "HOST2" || echo "HOST1" )
MIRROR="${!MIRROR_ID}"
SSH_KEY_PUB="${SSH_KEY}.pub"
STATE_FILE="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
AUTH_KEYS="/root/.ssh/authorized_keys"
MIRROR_SHORT="${MIRROR%%.*}"

START=$(date +%s)

echo ""


98	        continue
99	    fi
100
101	    # ── Pull: grab partner's conf from their disk → our local cache ──────────
102	    if [[ "$PUSH_ONLY" == false ]]; then
103	        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
104	        if [[ "$DRY_RUN" == true ]]; then
105	            warn "DRY RUN — would pull $partner_host:$remote_conf → $CACHE_DIR/${partner_slot}.conf"
106	        elif timeout "$SSH_TIMEOUT" scp -i "$SSH_KEY" \
107	            -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes -o StrictHostKeyChecking=no \

        continue
    fi

    # ── Pull: grab partner's conf from their disk → our local cache ──────────
    if [[ "$PUSH_ONLY" == false ]]; then
        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
        if [[ "$DRY_RUN" == true ]]; then
            warn "DRY RUN — would pull $partner_host:$remote_conf → $CACHE_DIR/${partner_slot}.conf"
        elif timeout "$SSH_TIMEOUT" scp -i "$SSH_KEY" \
            -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes -o StrictHostKeyChecking=no \


253	    echo "  Permissions set on .sh files"
254
255	    # ── Flash mode: sync Plugin/ to /boot/ so the webUI picks up updates ─────
256	    # In flash mode SCRIPTS_DIR is in appdata — Plugin/ lives in the repo there
257	    # but Unraid serves PHP from /boot/. Sync after every pull to keep them in step.
258	    _BOOT_DIR="/boot/config/plugins/varaverk"
259	    if [[ "$TARGET_DIR" != "$_BOOT_DIR" ]]; then
260	        echo ""
261	        echo "━━━ $ICON_SYNC Flash mode: sync Plugin/ → /boot/ ━━━"
262	        if rsync -a --delete "$TARGET_DIR/Plugin/" "$_BOOT_DIR/Plugin/" 2>/dev/null; then
263	            echo "  Plugin/ synced to /boot/ ✅"
264	        else

    echo "  Permissions set on .sh files"

    # ── Flash mode: sync Plugin/ to /boot/ so the webUI picks up updates ─────
    # In flash mode SCRIPTS_DIR is in appdata — Plugin/ lives in the repo there
    # but Unraid serves PHP from /boot/. Sync after every pull to keep them in step.
    _BOOT_DIR="/boot/config/plugins/varaverk"
    if [[ "$TARGET_DIR" != "$_BOOT_DIR" ]]; then
        echo ""
        echo "━━━ $ICON_SYNC Flash mode: sync Plugin/ → /boot/ ━━━"
        if rsync -a --delete "$TARGET_DIR/Plugin/" "$_BOOT_DIR/Plugin/" 2>/dev/null; then
            echo "  Plugin/ synced to /boot/ ✅"
        else


124	if ! platform_storage_healthy; then
125	    echo "Array not started — skipping watchdog cycle"
126	    exit 0
127	fi
128	log "$ICON_DISK Array: /mnt/user mounted (shfs) ✅"
129
130	# ==============================================================================================
131	# ━━━ Startup Grace ━━━

if ! platform_storage_healthy; then
    echo "Array not started — skipping watchdog cycle"
    exit 0
fi
log "$ICON_DISK Array: /mnt/user mounted (shfs) ✅"

# ==============================================================================================
# ━━━ Startup Grace ━━━


Now I have everything. Here's the complete picture before implementing:

**`master.conf`** — `STATE_DIR` and `DATA_DIR` have `/boot/config/plugins/varaverk` hardcoded. Since `SCRIPTS_DIR` is exported before master.conf loads, both become `${SCRIPTS_DIR}/State_Files` and `${SCRIPTS_DIR}/data` — same value on Unraid, correct on anything else. `DOCKER_APPDATA_BASE` is platform-specific and will move to the adapter.

**Adapter** — needs `platform_storage_path()` (`/mnt/user`), `platform_scripts_dir_probe_cmd()` (the `varaverk.cfg` grep), `platform_webui_install_path()` (the `/boot/config/plugins/varaverk` path for flash mode sync).

**`common.sh`** — `is_docker_enabled()` and `is_vm_manager_enabled()` read Unraid `.cfg` files directly — adapter already has `platform_is_service_enabled()`. `check_local_array()` and `check_remote_array()` hardcode `/mnt/user`. Tier 3 of `check_remote_disks()` same.

**`partnership_manager.sh`** — `check_both_healthy()` hardcodes `/mnt/user` twice. `do_final_sync()` has hardcoded `/mnt/user/appdata-Fallback/...` fallback paths — if `CRITICAL_SYNC_SHARES` isn't configured, it shouldn't silently use platform paths, it should warn.

**6 spot fixes** — `partnership_onboard.sh`, `partnership_transfer.sh` (remote SCRIPTS_DIR via `varaverk.cfg`), `onboard_cancel.sh` (setup file path), `conf_sync.sh` (remote conf path), `git_pull_execute.sh` (flash mode boot dir), `watchdog_orchestrator.sh` (log message).

Starting:


Edit

    DATA_DIR="/boot/config/plugins/varaverk/data"
    STATE_DIR="/boot/config/plugins/varaverk/State_Files"

    DATA_DIR="${SCRIPTS_DIR}/data"
    STATE_DIR="${SCRIPTS_DIR}/State_Files"


The file /boot/config/plugins/varaverk/Configurations/master.conf has been updated successfully. (file state is current in your context — no need to Read it back)

    DATA_DIR="/boot/config/plugins/varaverk/data"
    STATE_DIR="/boot/config/plugins/varaverk/State_Files"

    DATA_DIR="${SCRIPTS_DIR}/data"
    STATE_DIR="${SCRIPTS_DIR}/State_Files"

#   DATA_DIR   — historical logs, statistics, discovery histories, blocklists

#   STATE_DIR  — runtime state files for all scripts (watchdogs, fallback, transcode, etc.)

#                Requirement: ALL state files MUST use $STATE_DIR. No /tmp, no /boot/config root.

-    DATA_DIR="/boot/config/plugins/varaverk/data"

-    STATE_DIR="/boot/config/plugins/varaverk/State_Files"

+    STATE_DIR="${SCRIPTS_DIR}/State_Files"

# Controls behaviour when local and remote unRAID versions differ.


Now find `DOCKER_APPDATA_BASE` in master.conf and move it to the adapter:


136	# Runs before rsync — all nodes agree on tracked library before files are transferred.
137	# Remote API keys are read live from each node's config.xml via SSH — never stored here.
138	    CONF_SYNC_ENABLED=false             # HOST2 rebuild in progress — re-enable when HOST2 is back online
139
140	    ARR_SYNC_ENABLED=true
141	    ARR_SYNC_BLOCKLIST="${DATA_DIR}/arr_sync_blocklist.tsv"
142	    ARR_SYNC_CONNECT_TIMEOUT=10     # seconds — SSH connect timeout per node
143	    ARR_SYNC_API_TIMEOUT=60         # seconds — curl timeout for library fetches
144	    DOCKER_APPDATA_BASE="/mnt/user/appdata"
145	    ARR_SYNC_LIDARR_PORT=8686
146	    ARR_SYNC_SONARR_PORT=8989
147	    ARR_SYNC_RADARR_PORT=7878

# Runs before rsync — all nodes agree on tracked library before files are transferred.
# Remote API keys are read live from each node's config.xml via SSH — never stored here.
    CONF_SYNC_ENABLED=false             # HOST2 rebuild in progress — re-enable when HOST2 is back online

    ARR_SYNC_ENABLED=true
    ARR_SYNC_BLOCKLIST="${DATA_DIR}/arr_sync_blocklist.tsv"
    ARR_SYNC_CONNECT_TIMEOUT=10     # seconds — SSH connect timeout per node
    ARR_SYNC_API_TIMEOUT=60         # seconds — curl timeout for library fetches
    DOCKER_APPDATA_BASE="/mnt/user/appdata"
    ARR_SYNC_LIDARR_PORT=8686
    ARR_SYNC_SONARR_PORT=8989
    ARR_SYNC_RADARR_PORT=7878


    DOCKER_APPDATA_BASE="/mnt/user/appdata"
    ARR_SYNC_LIDARR_PORT=8686

    DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-}"   # set by platform adapter; override in host*.conf if needed
    ARR_SYNC_LIDARR_PORT=8686


    DOCKER_APPDATA_BASE="/mnt/user/appdata"
    ARR_SYNC_LIDARR_PORT=8686

    DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-}"   # set by platform adapter; override in host*.conf if needed
    ARR_SYNC_LIDARR_PORT=8686

ARR_SYNC_CONNECT_TIMEOUT=10     # seconds — SSH connect timeout per node

ARR_SYNC_API_TIMEOUT=60         # seconds — curl timeout for library fetches

-    DOCKER_APPDATA_BASE="/mnt/user/appdata"

+    DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-}"   # set by platform adapter; override in host*.conf if needed


Now add the three new functions to the adapter and set `DOCKER_APPDATA_BASE`:


#   platform_get_templates_dir    — path to Unraid CA docker templates-user directory
#   platform_rebuild_container    — rebuild a container from its stored XML template
#   platform_push_conf            — push master.conf to all listed hosts via WebGUI PHP
#   platform_push_setup_state     — push wizard setup state to WebGUI PHP

#   platform_storage_path         — root path for user shares/storage (e.g. /mnt/user)
#   platform_webui_install_path   — where the platform serves the WebGUI plugin files from
#   platform_scripts_dir_probe_cmd — shell command to run on a remote to discover its SCRIPTS_DIR
#   platform_get_templates_dir    — path to Unraid CA docker templates-user directory
#   platform_rebuild_container    — rebuild a container from its stored XML template
#   platform_push_conf            — push master.conf to all listed hosts via WebGUI PHP
#   platform_push_setup_state     — push wizard setup state to WebGUI PHP


The file /boot/config/plugins/varaverk/Plugin/unraid/adapter.sh has been updated successfully. (file state is current in your context — no need to Read it back)

#   platform_storage_path         — root path for user shares/storage (e.g. /mnt/user)
#   platform_webui_install_path   — where the platform serves the WebGUI plugin files from
#   platform_scripts_dir_probe_cmd — shell command to run on a remote to discover its SCRIPTS_DIR
#   platform_get_templates_dir    — path to Unraid CA docker templates-user directory
#   platform_rebuild_container    — rebuild a container from its stored XML template
#   platform_push_conf            — push master.conf to all listed hosts via WebGUI PHP
#   platform_push_setup_state     — push wizard setup state to WebGUI PHP

#   platform_is_mover_running     — unRAID mover process check

#   platform_stop_user_scripts    — kill all user.scripts background processes

#   platform_send_os_notification — native unRAID notify (dynamix)

+#   platform_storage_path         — root path for user shares/storage (e.g. /mnt/user)

+#   platform_webui_install_path   — where the platform serves the WebGUI plugin files from

+#   platform_scripts_dir_probe_cmd — shell command to run on a remote to discover its SCRIPTS_DIR


# ── Platform path defaults ─────────────────────────────────────────────────────────────────────
# Set here so master.conf and core scripts never contain OS-specific path literals.
# host*.conf may override any of these after the adapter is sourced.
DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}"

# ──────────────────────────────────────────────────────────────────────────────────────────────
# platform_storage_path
#   Writes the root path where user shares and storage are accessible to stdout.
#   Used by array health checks and disk verification.
# ──────────────────────────────────────────────────────────────────────────────────────────────
platform_storage_path() {
    echo "/mnt/user"
}

# ──────────────────────────────────────────────────────────────────────────────────────────────
# platform_webui_install_path
#   Writes the path where the platform serves WebGUI plugin files from.
#   On Unraid the PHP WebGUI is served from /boot/, separate from SCRIPTS_DIR.
#   Returns 1 (empty) if the platform serves directly from SCRIPTS_DIR (no sync needed).
# ──────────────────────────────────────────────────────────────────────────────────────────────
platform_webui_install_path() {
    echo "/boot/config/plugins/varaverk"
}

# ──────────────────────────────────────────────────────────────────────────────────────────────
# platform_scripts_dir_probe_cmd
#   Writes a shell command suitable for running on a remote via SSH to discover
#   that remote's SCRIPTS_DIR. Each platform exposes its install path differently.
# ──────────────────────────────────────────────────────────────────────────────────────────────
platform_scripts_dir_probe_cmd() {
    echo "grep -m1 'SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'"
}

platform_send_os_notification() {


# ── Platform path defaults ─────────────────────────────────────────────────────────────────────
# Set here so master.conf and core scripts never contain OS-specific path literals.
# host*.conf may override any of these after the adapter is sourced.
DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}"

# ──────────────────────────────────────────────────────────────────────────────────────────────
# platform_storage_path
#   Writes the root path where user shares and storage are accessible to stdout.
#   Used by array health checks and disk verification.
# ──────────────────────────────────────────────────────────────────────────────────────────────
platform_storage_path() {
    echo "/mnt/user"
}

# ──────────────────────────────────────────────────────────────────────────────────────────────
# platform_webui_install_path
#   Writes the path where the platform serves WebGUI plugin files from.
#   On Unraid the PHP WebGUI is served from /boot/, separate from SCRIPTS_DIR.
#   Returns 1 (empty) if the platform serves directly from SCRIPTS_DIR (no sync needed).
# ──────────────────────────────────────────────────────────────────────────────────────────────
platform_webui_install_path() {
    echo "/boot/config/plugins/varaverk"
}

# ──────────────────────────────────────────────────────────────────────────────────────────────
# platform_scripts_dir_probe_cmd
#   Writes a shell command suitable for running on a remote via SSH to discover
#   that remote's SCRIPTS_DIR. Each platform exposes its install path differently.
# ──────────────────────────────────────────────────────────────────────────────────────────────
platform_scripts_dir_probe_cmd() {
    echo "grep -m1 'SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'"
}

platform_send_os_notification() {

#   severity: normal | warning | alert  (default: normal)

#   Returns 1 if the notify script is absent.

+# ── Platform path defaults ─────────────────────────────────────────────────────────────────────

+# Set here so master.conf and core scripts never contain OS-specific path literals.

+# host*.conf may override any of these after the adapter is sourced.

+#   Writes the root path where user shares and storage are accessible to stdout.

+#   Used by array health checks and disk verification.

+#   Writes the path where the platform serves WebGUI plugin files from.

+#   On Unraid the PHP WebGUI is served from /boot/, separate from SCRIPTS_DIR.

+#   Returns 1 (empty) if the platform serves directly from SCRIPTS_DIR (no sync needed).

+    echo "/boot/config/plugins/varaverk"

+#   Writes a shell command suitable for running on a remote via SSH to discover

+#   that remote's SCRIPTS_DIR. Each platform exposes its install path differently.

+    echo "grep -m1 'SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'"


Now `common.sh` — fix the service checks and array checks:


# ==============================================================================================
# ── UNRAID SERVICE STATE ──────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Reads Unraid config files to check whether Docker and VM Manager are enabled.
# Use these guards before any script that manages containers or VMs.

is_docker_enabled() {
    [[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]]
}

is_vm_manager_enabled() {
    [[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]]
}

# ==============================================================================================
# ── PLATFORM SERVICE STATE ────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Check whether Docker and VM Manager are enabled on this host.
# Delegates to platform_is_service_enabled() from the platform adapter.

is_docker_enabled() {
    platform_is_service_enabled docker
}

is_vm_manager_enabled() {
    platform_is_service_enabled libvirt
}


The file /boot/config/plugins/varaverk/common.sh has been updated successfully. (file state is current in your context — no need to Read it back)

# ==============================================================================================
# ── UNRAID SERVICE STATE ──────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Reads Unraid config files to check whether Docker and VM Manager are enabled.
# Use these guards before any script that manages containers or VMs.

is_docker_enabled() {
    [[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]]
}

is_vm_manager_enabled() {
    [[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]]
}

# ==============================================================================================
# ── PLATFORM SERVICE STATE ────────────────────────────────────────────────────────────────────
# ==============================================================================================
# Check whether Docker and VM Manager are enabled on this host.
# Delegates to platform_is_service_enabled() from the platform adapter.

is_docker_enabled() {
    platform_is_service_enabled docker
}

is_vm_manager_enabled() {
    platform_is_service_enabled libvirt
}

-# ── UNRAID SERVICE STATE ──────────────────────────────────────────────────────────────────────

+# ── PLATFORM SERVICE STATE ────────────────────────────────────────────────────────────────────

-# Reads Unraid config files to check whether Docker and VM Manager are enabled.

-# Use these guards before any script that manages containers or VMs.

+# Check whether Docker and VM Manager are enabled on this host.

+# Delegates to platform_is_service_enabled() from the platform adapter.

-    [[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]]

-    [[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]]

+    platform_is_service_enabled libvirt


# Verifies local /mnt/user is mounted and has shares.
# Non-fatal — returns status for caller to decide.
# Used by fallback before starting remote containers locally.
check_local_array() {
    log "Checking local array..."
    if ! mountpoint -q /mnt/user 2>/dev/null; then
        error "$ICON_DISK Local array is not started — /mnt/user is not mounted"
        return 1
    fi
    local file_count
    file_count=$(ls /mnt/user 2>/dev/null | wc -l)
    if [[ "$file_count" -eq 0 ]]; then
        error "$ICON_DISK Local array appears empty — shares may not be available"
        return 1
    fi
    log "Local array is healthy"
    return 0
}

# Verifies local storage is mounted and has shares.
# Non-fatal — returns status for caller to decide.
# Used by fallback before starting remote containers locally.
check_local_array() {
    log "Checking local array..."
    if ! platform_storage_healthy; then
        error "$ICON_DISK Local array is not started — storage not mounted"
        return 1
    fi
    local _storage_path file_count
    _storage_path=$(platform_storage_path)
    file_count=$(ls "$_storage_path" 2>/dev/null | wc -l)
    if [[ "$file_count" -eq 0 ]]; then
        error "$ICON_DISK Local array appears empty — shares may not be available"
        return 1
    fi
    log "Local array is healthy"
    return 0
}


# Verifies local /mnt/user is mounted and has shares.
# Non-fatal — returns status for caller to decide.
# Used by fallback before starting remote containers locally.
check_local_array() {
    log "Checking local array..."
    if ! mountpoint -q /mnt/user 2>/dev/null; then
        error "$ICON_DISK Local array is not started — /mnt/user is not mounted"
        return 1
    fi
    local file_count
    file_count=$(ls /mnt/user 2>/dev/null | wc -l)
    if [[ "$file_count" -eq 0 ]]; then
        error "$ICON_DISK Local array appears empty — shares may not be available"
        return 1
    fi
    log "Local array is healthy"
    return 0
}

# Verifies local storage is mounted and has shares.
# Non-fatal — returns status for caller to decide.
# Used by fallback before starting remote containers locally.
check_local_array() {
    log "Checking local array..."
    if ! platform_storage_healthy; then
        error "$ICON_DISK Local array is not started — storage not mounted"
        return 1
    fi
    local _storage_path file_count
    _storage_path=$(platform_storage_path)
    file_count=$(ls "$_storage_path" 2>/dev/null | wc -l)
    if [[ "$file_count" -eq 0 ]]; then
        error "$ICON_DISK Local array appears empty — shares may not be available"
        return 1
    fi
    log "Local array is healthy"
    return 0
}

# ── LOCAL HEALTH CHECKS ───────────────────────────────────────────────────────────────────────

-# Verifies local /mnt/user is mounted and has shares.

+# Verifies local storage is mounted and has shares.

-    if ! mountpoint -q /mnt/user 2>/dev/null; then

-        error "$ICON_DISK Local array is not started — /mnt/user is not mounted"

+    if ! platform_storage_healthy; then

+        error "$ICON_DISK Local array is not started — storage not mounted"

-    file_count=$(ls /mnt/user 2>/dev/null | wc -l)

+    _storage_path=$(platform_storage_path)

+    file_count=$(ls "$_storage_path" 2>/dev/null | wc -l)


# Verifies remote /mnt/user is mounted via SSH.
# Non-fatal — returns status. Used before handback rsync.
# Syncing to remote with no array fills rootfs rapidly.
check_remote_array() {
    log "Checking remote array on $REMOTE_SERVER_NAME..."
    local result
    result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
        "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
    if [[ "$result" != "yes" ]]; then
        error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME"
        return 1
    fi
    log "Remote array is healthy"
    return 0
}

# Verifies remote storage is mounted via SSH.
# Non-fatal — returns status. Used before handback rsync.
# Syncing to remote with no array fills rootfs rapidly.
check_remote_array() {
    log "Checking remote array on $REMOTE_SERVER_NAME..."
    local _spath result
    _spath=$(platform_storage_path)
    result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
        "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)
    if [[ "$result" != "yes" ]]; then
        error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME"
        return 1
    fi
    log "Remote array is healthy"
    return 0
}


# Verifies remote /mnt/user is mounted via SSH.
# Non-fatal — returns status. Used before handback rsync.
# Syncing to remote with no array fills rootfs rapidly.
check_remote_array() {
    log "Checking remote array on $REMOTE_SERVER_NAME..."
    local result
    result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
        "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
    if [[ "$result" != "yes" ]]; then
        error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME"
        return 1
    fi
    log "Remote array is healthy"
    return 0
}

# Verifies remote storage is mounted via SSH.
# Non-fatal — returns status. Used before handback rsync.
# Syncing to remote with no array fills rootfs rapidly.
check_remote_array() {
    log "Checking remote array on $REMOTE_SERVER_NAME..."
    local _spath result
    _spath=$(platform_storage_path)
    result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
        "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)
    if [[ "$result" != "yes" ]]; then
        error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME"
        return 1
    fi
    log "Remote array is healthy"
    return 0
}

# ── REMOTE HEALTH CHECKS ─────────────────────────────────────────────────────────────────────

-# Verifies remote /mnt/user is mounted via SSH.

+# Verifies remote storage is mounted via SSH.

-        "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)

+        "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)


Now fix Tier 3 of `check_remote_disks()`:


996	        "zpool list -H -o name 2>/dev/null | while read pool; do
997	            [[ -d \"/mnt/\${pool}/$share_name\" ]] && echo \"\$pool\" && continue
998	            find \"/mnt/\${pool}\" -maxdepth 2 -name '$share_name' -type d 2>/dev/null | \
999	                grep -q . && echo \"\$pool\"
1000	        done | sort -u" 2>/dev/null)
1001
1002	    # Tier 3 — shfs fallback via /mnt/user
1003	    if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
1004	        local on_user
1005	        on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
1006	            "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
1007	            2>/dev/null)
1008	        if [[ -n "$on_user" ]]; then
1009	            local actual_path
1010	            actual_path=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
1011	                "readlink -f '$on_user' 2>/dev/null || echo '$on_user'" 2>/dev/null)
1012	            local pool_name
1013	            pool_name=$(echo "$actual_path" | awk -F/ '{print $3}')
1014	            if [[ -n "$pool_name" ]] && [[ "$pool_name" != "user" ]]; then
1015	                zfs_pool_paths="$pool_name"
1016	                info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME"
1017	            else
1018	                # Can't determine pool — verify shfs itself is mounted
1019	                local shfs_ok
1020	                shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
1021	                    "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
1022	                if [[ "$shfs_ok" == "yes" ]]; then
1023	                    success "All disks backing $share_name are online ✅ (via shfs)"
1024	                    return 0
1025	                fi

        "zpool list -H -o name 2>/dev/null | while read pool; do
            [[ -d \"/mnt/\${pool}/$share_name\" ]] && echo \"\$pool\" && continue
            find \"/mnt/\${pool}\" -maxdepth 2 -name '$share_name' -type d 2>/dev/null | \
                grep -q . && echo \"\$pool\"
        done | sort -u" 2>/dev/null)

    # Tier 3 — shfs fallback via /mnt/user
    if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
        local on_user
        on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
            "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
            2>/dev/null)
        if [[ -n "$on_user" ]]; then
            local actual_path
            actual_path=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                "readlink -f '$on_user' 2>/dev/null || echo '$on_user'" 2>/dev/null)
            local pool_name
            pool_name=$(echo "$actual_path" | awk -F/ '{print $3}')
            if [[ -n "$pool_name" ]] && [[ "$pool_name" != "user" ]]; then
                zfs_pool_paths="$pool_name"
                info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME"
            else
                # Can't determine pool — verify shfs itself is mounted
                local shfs_ok
                shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                    "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
                if [[ "$shfs_ok" == "yes" ]]; then
                    success "All disks backing $share_name are online ✅ (via shfs)"
                    return 0
                fi


    # Tier 3 — shfs fallback via /mnt/user
    if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
        local on_user
        on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
            "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
            2>/dev/null)
        if [[ -n "$on_user" ]]; then
            local actual_path
            actual_path=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                "readlink -f '$on_user' 2>/dev/null || echo '$on_user'" 2>/dev/null)
            local pool_name
            pool_name=$(echo "$actual_path" | awk -F/ '{print $3}')
            if [[ -n "$pool_name" ]] && [[ "$pool_name" != "user" ]]; then
                zfs_pool_paths="$pool_name"
                info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME"
            else
                # Can't determine pool — verify shfs itself is mounted
                local shfs_ok
                shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                    "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
                if [[ "$shfs_ok" == "yes" ]]; then
                    success "All disks backing $share_name are online ✅ (via shfs)"
                    return 0
                fi

    # Tier 3 — storage root fallback
    local _spath
    _spath=$(platform_storage_path)
    if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
        local on_user
        on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
            "find '$_spath' -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
            2>/dev/null)
        if [[ -n "$on_user" ]]; then
            local actual_path
            actual_path=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                "readlink -f '$on_user' 2>/dev/null || echo '$on_user'" 2>/dev/null)
            local pool_name
            pool_name=$(echo "$actual_path" | awk -F/ '{print $3}')
            if [[ -n "$pool_name" ]] && [[ "$pool_name" != "user" ]]; then
                zfs_pool_paths="$pool_name"
                info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME"
            else
                # Can't determine pool — verify storage root itself is mounted
                local shfs_ok
                shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                    "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)
                if [[ "$shfs_ok" == "yes" ]]; then
                    success "All disks backing $share_name are online ✅ (via storage root)"
                    return 0
                fi


    # Tier 3 — shfs fallback via /mnt/user
    if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
        local on_user
        on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
            "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
            2>/dev/null)
        if [[ -n "$on_user" ]]; then
            local actual_path
            actual_path=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                "readlink -f '$on_user' 2>/dev/null || echo '$on_user'" 2>/dev/null)
            local pool_name
            pool_name=$(echo "$actual_path" | awk -F/ '{print $3}')
            if [[ -n "$pool_name" ]] && [[ "$pool_name" != "user" ]]; then
                zfs_pool_paths="$pool_name"
                info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME"
            else
                # Can't determine pool — verify shfs itself is mounted
                local shfs_ok
                shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                    "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
                if [[ "$shfs_ok" == "yes" ]]; then
                    success "All disks backing $share_name are online ✅ (via shfs)"
                    return 0
                fi

    # Tier 3 — storage root fallback
    local _spath
    _spath=$(platform_storage_path)
    if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
        local on_user
        on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
            "find '$_spath' -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
            2>/dev/null)
        if [[ -n "$on_user" ]]; then
            local actual_path
            actual_path=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                "readlink -f '$on_user' 2>/dev/null || echo '$on_user'" 2>/dev/null)
            local pool_name
            pool_name=$(echo "$actual_path" | awk -F/ '{print $3}')
            if [[ -n "$pool_name" ]] && [[ "$pool_name" != "user" ]]; then
                zfs_pool_paths="$pool_name"
                info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME"
            else
                # Can't determine pool — verify storage root itself is mounted
                local shfs_ok
                shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                    "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)
                if [[ "$shfs_ok" == "yes" ]]; then
                    success "All disks backing $share_name are online ✅ (via storage root)"
                    return 0
                fi

-    # Tier 3 — shfs fallback via /mnt/user

-            "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \

+            "find '$_spath' -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \

-                # Can't determine pool — verify shfs itself is mounted

+                # Can't determine pool — verify storage root itself is mounted

-                    "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)

+                    "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)

-                    success "All disks backing $share_name are online ✅ (via shfs)"

+                    success "All disks backing $share_name are online ✅ (via storage root)"


Conversation compacted


This session is being continued from a previous conversation that ran out of context. The summary below covers the earlier portion of the conversation.

Summary:
1. Primary Request and Intent:

**Partnership Services Stack (completed):**
- Add `PARTNERSHIP_SERVICES_STACK` to the onboarding system alongside auth and arr stacks
- Services: `my-Emby.xml`, `my-jellyfin.xml`, `my-Seerr.xml`, `my-SeerrFin.xml`
- User confirmed "jellyfin and seerr and seerrfin" for the service names

**Unraid-specific code removal from core (completed first wave — partnership scripts, second wave — all scripts):**
- User: "now this is in the core code. all bash scripts are core code. i need unraid specific removed and added to the app layer. the app layer calls on the core code and is built for specific os's. this one is unraid. next might be truenas, who knows"
- User: "lets check all bash scripts"
- User re gitea: "gitea is no longer involved. deployment is with github and handled elsewhere" then "leave it alone im using it for dev, but not in release"

**Platform-agnostic architecture refactor (active/in-progress):**
- User: "how do we make this work with other systems. goal was to have plugins/adapters. upon install os is detected and points to the correct folder in plugins.... nothing can really be hard coded right.... just thinking out loud"
- User: "lets do it.... ive gotten close to the core scripts being universal, then app layer and adapters make everything work.. long term goal is running on other distros, and being able to cross communicate truenas and unraid and say ubuntu......"
- Goal: eliminate ALL hardcoded OS-specific paths from core scripts; all platform-specific values come from `Plugin/$PLATFORM/adapter.sh`

2. Key Technical Concepts:
- Varaverk: self-healing two-server Unraid home media ecosystem (HOST1=unRAID-Gmer4Lfe, HOST2=unRAID-Jayred365)
- Three-file config model: `master.conf` (shared), `host1.conf`, `host2.conf` (per-host)
- Platform adapter layer: `Plugin/$PLATFORM/adapter.sh` provides `platform_*()` API; core scripts never branch on OS
- `load_config.sh` already does runtime OS detection: `/etc/unraid-version` → `PLATFORM="unraid"`, `/etc/truenas` → `PLATFORM="truenas"`; exports `SCRIPTS_DIR` = repo root (self-determined)
- `SCRIPTS_DIR` is exported BEFORE master.conf is sourced, so master.conf can use `${SCRIPTS_DIR}` for paths
- Partnership onboarding: owner/mirror model; 10-step OWNER PATH deploying auth→arr→services stacks
- Sub-adapter: `Plugin/$PLATFORM/Partnership/containers.sh` — container-specific platform operations
- `detect_hosts()` in `common.sh` sets `MY_ID`/`REMOTE_ID` and aliases `HOST*_VAR` → `VAR` via `_alias_array`
- Sparse git checkout: each server only gets its own `host*.conf`
- Cross-platform remote check challenge: SSH commands running on remote server need that server's platform-specific paths — currently solved with `platform_storage_path()` (works for same-platform pairs, future work for true cross-platform)

3. Files and Code Sections:

**`/boot/config/plugins/varaverk/Configurations/host1.conf`**
- Added `HOST1_PARTNERSHIP_SERVICES_STACK` with 4 XML entries and empty `HOST1_PARTNERSHIP_SERVICES_REPLACE_CONTAINERS`
- Added after `HOST1_PARTNERSHIP_ARR_REPLACE_CONTAINERS`
```bash
    HOST1_PARTNERSHIP_SERVICES_STACK=(
        "my-Emby.xml"
        "my-jellyfin.xml"
        "my-Seerr.xml"
        "my-SeerrFin.xml"
    )
    HOST1_PARTNERSHIP_SERVICES_REPLACE_CONTAINERS=(
    )
```

**`/boot/config/plugins/varaverk/common.sh`**
- `_alias_array` calls added for `PARTNERSHIP_SERVICES_STACK` and `PARTNERSHIP_SERVICES_REPLACE_CONTAINERS` (after `PARTNERSHIP_ARR_REPLACE_CONTAINERS`)
- `notify()` — replaced hardcoded dynamix path block with `platform_send_os_notification()`
- `get_unraid_temp_thresholds()` — now delegates to `platform_get_temp_thresholds()`:
```bash
get_unraid_temp_thresholds() {
    read -r UNRAID_DISK_HOT UNRAID_DISK_MAX UNRAID_SSD_HOT UNRAID_SSD_MAX \
        <<< "$(platform_get_temp_thresholds)"
    UNRAID_DISK_HOT="${UNRAID_DISK_HOT:-${SMART_TEMP_WARN:-45}}"
    UNRAID_DISK_MAX="${UNRAID_DISK_MAX:-${SMART_TEMP_CRIT:-55}}"
    UNRAID_SSD_HOT="${UNRAID_SSD_HOT:-60}"
    UNRAID_SSD_MAX="${UNRAID_SSD_MAX:-70}"
}
```
- `check_local_disk_temps()` — reads via `platform_get_disk_states()`, loops with `<<< "$disks_ini_content"`
- `check_remote_disks()` — uses `platform_disk_states_path()` for SSH command path; error message updated
- Section "UNRAID SERVICE STATE" renamed to "PLATFORM SERVICE STATE"
- `is_docker_enabled()` → `platform_is_service_enabled docker`
- `is_vm_manager_enabled()` → `platform_is_service_enabled libvirt`
- `check_local_array()` — uses `platform_storage_healthy()` and `platform_storage_path()`:
```bash
check_local_array() {
    log "Checking local array..."
    if ! platform_storage_healthy; then
        error "$ICON_DISK Local array is not started — storage not mounted"
        return 1
    fi
    local _storage_path file_count
    _storage_path=$(platform_storage_path)
    file_count=$(ls "$_storage_path" 2>/dev/null | wc -l)
    ...
}
```
- `check_remote_array()` — uses `platform_storage_path()` in SSH command:
```bash
check_remote_array() {
    local _spath result
    _spath=$(platform_storage_path)
    result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
        "mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)
    ...
}
```

**`/boot/config/plugins/varaverk/Plugin/unraid/adapter.sh`**
- Fixed `platform_get_temp_thresholds()` — was reading wrong keys (`diskWarn`/`diskCrit`), now reads `hot`/`max`/`hotssd`/`maxssd`, returns 4 space-separated values: HDD_HOT HDD_MAX SSD_HOT SSD_MAX
- Added `platform_disk_states_path()` → `echo "/var/local/emhttp/disks.ini"`
- Updated `platform_get_disk_states()` to call `platform_disk_states_path()`
- Added platform path defaults section set at source time:
```bash
DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}"
```
- Added `platform_storage_path()`:
```bash
platform_storage_path() {
    echo "/mnt/user"
}
```
- Added `platform_webui_install_path()`:
```bash
platform_webui_install_path() {
    echo "/boot/config/plugins/varaverk"
}
```
- Added `platform_scripts_dir_probe_cmd()`:
```bash
platform_scripts_dir_probe_cmd() {
    echo "grep -m1 'SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'"
}
```
- Added `platform_get_templates_dir()` → `echo "/boot/config/plugins/dockerMan/templates-user"`
- Added `platform_rebuild_container()` → wraps `/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container`
- Added `platform_push_conf()` → wraps PHP `vv_push_master_conf()`, returns 0/1 based on push result
- Added `platform_push_setup_state()` → wraps PHP `vv_push_setup_state()`, always returns 0

**`/boot/config/plugins/varaverk/Plugin/unraid/Partnership/containers.sh`**
- Added `PARTNERSHIP_SERVICES_STACK` to both `cleanup_deployed_stack_on_remote()` and `cleanup_deployed_stack_locally()`
- `cleanup_deployed_stack_locally()` gains a third SSH read for `PARTNERSHIP_SERVICES_STACK`; xml_names now includes all three stacks
- Moved `reconfigure_webui()` from `partnership_manager.sh` — uses `$TEMPLATES_DIR` not hardcoded path
- Moved `reconfigure_local_webuis()` from `partnership_manager.sh` — uses `$TEMPLATES_DIR`
- Log strings updated: "auth/arr stacks" → "auth/arr/services stacks"

**`/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh`**
- Header OWNER PATH: 8 steps → 10 steps; added Steps 6+7 in description
- CONFIGURATION section: added `HOST*_PARTNERSHIP_SERVICES_STACK` and `HOST*_PARTNERSHIP_SERVICES_REPLACE_CONTAINERS` docs
- RUNTIME MODES: added `--skip-services-stack` doc; updated step references
- Flag parsing: added `SKIP_SERVICES_STACK=false`; added `--skip-services-stack) SKIP_SERVICES_STACK=true ;;`
- Tracking vars added: `STEP_STOP_SERVICES_OK=true`, `STEP_SERVICES_OK=true`, `SERVICES_DEPLOYED=0`, `SERVICES_FAILED=0`
- Added Step 6 (Stop Mirror Services Stack) and Step 7 (Deploy Services Stack) blocks
- Old steps renumbered: 6→8 (Partnership Onboard), 7→9 (Arr Bootstrap), 8→10 (Conf Push)
- Summary block updated with 10 steps
- `write_onboard_phase()`: PHP call → `platform_push_setup_state`
- Phase 1 conf push block: PHP block → `platform_push_conf()` + `platform_push_setup_state()`
- Step 10 conf push block: PHP block → `platform_push_conf()` + `platform_push_setup_state()`

**`/boot/config/plugins/varaverk/Partnership/partnership_manager.sh`**
- Added `source "$SCRIPT_DIR/../Plugin/$PLATFORM/Partnership/containers.sh"` after `load_config.sh` source
- Removed `platform_require_cmd` for notify script
- Removed `reconfigure_webui()` function (moved to containers.sh adapter)
- Removed `reconfigure_local_webuis()` function (moved to containers.sh adapter)
- PARTNERSHIP_ENABLED=true block: PHP → `sed -i` + `platform_push_conf()`:
```bash
local _master_conf="${SCRIPTS_ROOT:-$(dirname "$SCRIPT_DIR")}/master.conf"
if grep -q "^[[:space:]]*PARTNERSHIP_ENABLED=" "$_master_conf" 2>/dev/null; then
    sed -i "s|^[[:space:]]*PARTNERSHIP_ENABLED=.*|PARTNERSHIP_ENABLED=true|" "$_master_conf"
else
    echo "PARTNERSHIP_ENABLED=true" >> "$_master_conf"
fi
log "PARTNERSHIP_ENABLED=true in master.conf ✅"
platform_push_conf | while IFS= read -r line; do log "$line"; done
```
- Setup state push: `platform_push_setup_state`

**`/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh`**
- CONFIGURATION section: added `HOST*_PARTNERSHIP_SERVICES_STACK` docs

**`/boot/config/plugins/varaverk/Partnership/ssh_setup.sh`** and **`/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh`**
- Removed redundant `platform_require_cmd` for dynamix notify script

**`/boot/config/plugins/varaverk/Docker_Essentials/docker_update.sh`** and **`docker_update_remaining.sh`**
- `if /usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container "$container"` → `if platform_rebuild_container "$container"`

**`/boot/config/plugins/varaverk/Watchdogs/docker_watchdog.sh`**
- Removed `platform_require_cmd "/etc/rc.d/rc.docker"` check (adapter handles missing rc script gracefully)
- Removed `platform_require_cmd` for notify script (mass removal)

**44 files — mass removal via Perl**
- All had the pattern:
```bash
platform_require_cmd \
    "/usr/local/emhttp/plugins/dynamix/scripts/notify" \
    "" "" \
    "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
```
- Perl script matched both multi-line (4-line) and single-line variants
- Files spanned: Docker_Essentials, Fallback, Media, Monitors, Orchestrators, Rsync, System_Essentials, Tools, Transcodes, Watchdogs

**`/boot/config/plugins/varaverk/Configurations/master.conf`**
- `DATA_DIR="/boot/config/plugins/varaverk/data"` → `DATA_DIR="${SCRIPTS_DIR}/data"`
- `STATE_DIR="/boot/config/plugins/varaverk/State_Files"` → `STATE_DIR="${SCRIPTS_DIR}/State_Files"`
- `DOCKER_APPDATA_BASE="/mnt/user/appdata"` → `DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-}"` (adapter sets the default now)
- Note: `SCRIPTS_DIR` is exported in `load_config.sh` BEFORE master.conf is sourced, so `${SCRIPTS_DIR}` is valid here. On Unraid, `SCRIPTS_DIR=/boot/config/plugins/varaverk`, so these resolve to the same values as before — no behavior change on Unraid.

4. Errors and fixes:
- `docker_update_remaining.sh` Edit failed with "File has not been read yet" — fixed by reading the file first before editing
- `platform_get_temp_thresholds()` in adapter was reading wrong config keys (`diskWarn`/`diskCrit` instead of `hot`/`max`/`hotssd`/`maxssd`) and only returning 2 values instead of 4 — corrected during refactor

5. Problem Solving:
- **Cross-platform remote SSH commands**: When SSH commands run on the remote server, they need that remote server's platform-specific paths. Solved for same-platform pairs using `platform_storage_path()` (local adapter provides value, same value on same-platform peer). True cross-platform (Unraid→TrueNAS) requires either a shared convention file or `REMOTE_STORAGE_PATH` config variable — deferred as future work.
- **Chicken-and-egg with master.conf and adapter**: master.conf is sourced BEFORE the adapter in load_config.sh. So adapter can't pre-set values that master.conf uses. Solution: master.conf uses `${SCRIPTS_DIR}` for paths (SCRIPTS_DIR is set before master.conf loads). Adapter sets `DOCKER_APPDATA_BASE` which master.conf leaves as `${DOCKER_APPDATA_BASE:-}` — master.conf's empty default is overridden after adapter sources.
- **`platform_get_temp_thresholds()` expansion**: Original adapter function only returned 2 values with wrong key names. Expanded to return all 4 values (HDD_HOT HDD_MAX SSD_HOT SSD_MAX) matching the correct dynamix.cfg key names used by the working `get_unraid_temp_thresholds()` function.

6. All user messages:
- "ok so now we have auth,arrs,and shared services that get pushed to mirrors during onboarding"
- "now this is in the core code. all bash scripts are core code. i need unraid specific removed and added to the app layer. the app layer calls on the core code and is built for specific os's. this one is unraid. next might be truenas, who knows"
- "gitea is no longer involved. deployment is with github and handled else where"
- "leave it alone im using it for dev, but not in release"
- "lets check all bash scripts"
- "how do we make this work with other systems. goal was to have plugins/adapters. upon install os is detected and points to the correct folder in plugins.... nothing can really be hard coded right.... just thinking out loud"
- "lets do it.... ive gotton close to the cor scipts being universal, then app layer and adpaters make everything work.. long term goal is running on other distros, and being able be cross comunicate truas and unraid and say ubuntu......"

Security/operational constraints (preserved verbatim):
- **Workspace is always** `/boot/config/plugins/varaverk` — every edit goes here
- **Never touch** `/mnt/user/Important Shit/Git/Development/Varaverk` — stale dev folder, ignore it
- **No Co-Authored-By** in commit messages unless explicitly asked
- `gitea_ssh_setup.sh` — leave alone, used for dev not release, excluded from all refactoring

7. Pending Tasks:
- **`common.sh` `check_remote_disks()` Tier 3** — two hardcoded `/mnt/user` references in SSH commands (lines ~1006 and ~1021) need `platform_storage_path()`:
  - `find /mnt/user -maxdepth 3 -name '$share_name' -type d`
  - `mountpoint -q /mnt/user && echo yes || echo no`
- **`partnership_manager.sh` `check_both_healthy()`** — two `/mnt/user` hardcodes:
  - Line ~877: `mountpoint -q /mnt/user 2>/dev/null` → `platform_storage_healthy`
  - Line ~885: `"mountpoint -q /mnt/user && timeout 10 docker ps"` → use `platform_storage_path()`
- **`partnership_manager.sh` `do_final_sync()`** — hardcoded fallback paths (lines ~909-912):
  - `/mnt/user/appdata-Fallback/Critical-Data`
  - `/mnt/user/Media_Server/Emby`
  - Fix: warn if `CRITICAL_SYNC_SHARES` not configured instead of using platform-specific fallbacks
- **`partnership_onboard.sh:315-316`** — remote SCRIPTS_DIR discovery via hardcoded varaverk.cfg:
  ```bash
  'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'"'
  OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"
  ```
  → Use `platform_scripts_dir_probe_cmd()` for the grep command; default fallback → `$SCRIPTS_DIR`
- **`partnership_transfer.sh:291-293`** — same varaverk.cfg pattern:
  ```bash
  "grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'"
  _REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}"
  ```
  → Same fix as onboard
- **`onboard_cancel.sh:91`** — hardcoded `/boot/config/varaverk_setup.db`:
  ```bash
  sed -i "/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" /boot/config/varaverk_setup.db 2>/dev/null
  ```
  → Use `${STATE_DIR}` or `${VARAVERK_SETUP_FILE}`
- **`conf_sync.sh:103`** — `remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"` → use `$SCRIPTS_DIR`
- **`git_pull_execute.sh:258`** — `_BOOT_DIR="/boot/config/plugins/varaverk"` → use `platform_webui_install_path()`
- **`watchdog_orchestrator.sh:128`** — log message `"Array: /mnt/user mounted (shfs) ✅"` → use `platform_storage_path()` or make generic

8. Current Work:
Immediately before this summary request, the work was on the platform-agnostic refactor of core scripts. The following changes had just been completed:
- `master.conf` updated (STATE_DIR, DATA_DIR use SCRIPTS_DIR; DOCKER_APPDATA_BASE cleared to let adapter set it)
- `Plugin/unraid/adapter.sh` updated with `platform_storage_path()`, `platform_webui_install_path()`, `platform_scripts_dir_probe_cmd()`, and `DOCKER_APPDATA_BASE` default
- `common.sh` updated: service check functions now use adapter; `check_local_array()` and `check_remote_array()` now use `platform_storage_healthy()` and `platform_storage_path()`

The Read tool had just returned the context for `check_remote_disks()` Tier 3 (lines 996-1025 of common.sh), showing the hardcoded `/mnt/user` in:
- Line ~1006: `"find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1"`
- Line ~1021: `"mountpoint -q /mnt/user && echo yes || echo no"`

The session was cut off mid-edit at this exact point.

9. Optional Next Step:
Continue the platform-agnostic refactor exactly where it left off. The next edit is `check_remote_disks()` Tier 3 in `common.sh` — replace the two hardcoded `/mnt/user` references with `platform_storage_path()`:

From the last Read output (lines 1002-1025 of common.sh):
```bash
    # Tier 3 — shfs fallback via /mnt/user
    if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
        local on_user
        on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
            "find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
            2>/dev/null)
        ...
                shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
                    "mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
```

These two lines need to use `platform_storage_path()` (called locally, value embedded in the SSH command string, works for same-platform pairs).

After that, continue with the remaining pending tasks listed above in order.

If you need specific details from before compaction (like exact code snippets, error messages, or content you generated), read the full transcript at: /root/.claude/projects/-root/685e6c5b-62c1-40bd-9cfd-2c9f7e15c50f.jsonl
Continue the conversation from where it left off without asking the user any further questions. Resume directly — do not acknowledge the summary, do not recap what was happening, do not preface with "I'll continue" or similar. Pick up the last task as if the break never happened.