1	<?xml version='1.0' standalone='yes'?>
2	<!DOCTYPE PLUGIN [
3	<!ENTITY name    "varaverk">
4	<!ENTITY author  "gmer4lfe">
5	<!ENTITY version "2026.05.31">
6	<!ENTITY sha256  "d588470d6cc7f284601cb56039d5dfea6fb5bb1ee2c800657438a8edacafbd01">
7	<!ENTITY launch  "varaverk/monitor">
8	<!ENTITY github  "https://github.com/FailedProxy/Varaverk">
9	<!ENTITY branch  "main">
10	<!ENTITY cfgdir  "/boot/config/plugins/varaverk">
11	<!ENTITY plugdir "/usr/local/emhttp/plugins/varaverk">
12	<!ENTITY pkg     "varaverk-&version;-noarch-1.txz">
13	]>
14	<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;"
15	        support="https://github.com/FailedProxy/Varaverk/issues"
16	        icon="/plugins/varaverk/icons/varaverk.png">
17
18	<CHANGES>
19	###2026.05.31
20	- Packaged release: web files now ship as a .txz that Unraid reinstalls to RAM on every boot
21	- Survives reboots with zero manual steps (no symlink, no go script) — fixes plugin vanishing after OS upgrades
22	- Scripts are git-cloned to appdata on first install; web files stay on flash (~200KB)
23	- Updates handled in-UI (git pull); the plugin no longer pulls on every boot
24
25	###2026.05.30
26	- First-run setup wizard: auto-detects hostname, creates master.conf + host conf from templates
27	- Scheduler setup mode: after wizard, master.conf and host conf open sequentially with forced save flow
28	- Partnership tab: Onboard button highlighted on arrival from wizard; disabled until partner is configured
29	- HOST2 install paths: state-file pull, master.conf push detection, conf-only flow
30	- Onboard Step 9: master.conf automatically pushed to all listed hosts on onboard completion
31	- Graceful pre-onboard state: neutral banners instead of error warnings before SSH is configured
32	- GitHub link in tab bar and Settings page; Community Apps support URL
33
34	###2026.05.28
35	- Initial release: Monitor, Scheduler, Docker, Watchdog, Partnership, Fallback, Arrs tabs
36	- Mutual container fallback with tiered escalation and strike-confirmed handback
37	- Partnership lifecycle: onboard, offboard, transfer
38	- Rsync profile system with per-share container stops and writeback
39	- Watchdog: Tier 1 (explicit) + Tier 2 (global scan) container monitoring
40	- master.conf push-on-save to all configured partners via SSH
41	</CHANGES>
42
43	<!--
44	  ── 1. Web files symlink (runs on every boot) ──────────────────────────────────
45	  Instead of extracting a .txz, we symlink the installed plugin web dir directly
46	  to the workspace on flash. Changes to Plugin/unraid/ are live instantly — no
47	  package build, no sync step. /boot is always mounted before this runs.
48	  ────────────────────────────────────────────────────────────────────────────────
49	-->
50	<FILE Run="/bin/bash">
51	<INLINE>
52	<![CDATA[
53	#!/bin/bash
54	WEB_DIR="/usr/local/emhttp/plugins/varaverk"
55	SRC="/boot/config/plugins/varaverk/Plugin/unraid"
56	[[ -L "$WEB_DIR" ]] && rm -f "$WEB_DIR"
57	[[ -d "$WEB_DIR" ]] && rm -rf "$WEB_DIR"
58	ln -sf "$SRC" "$WEB_DIR"
59	echo "[Varaverk] web dir symlinked → $SRC"
60	]]>
61	</INLINE>
62	</FILE>
63
64	<!--
65	  ── 2. Scripts bootstrap (first install only) ──────────────────────────────────
66	  Clones the repo directly into the plugin config dir on flash (/boot/config/plugins/varaverk).
67	  No array dependency — scripts live on flash (64GB NVMe) and are available at boot.
68	  Uses git init+fetch+reset so the clone works into the non-empty cfgdir (varaverk.cfg,
69	  varaverk-*.txz etc. are already there). Never auto-pulls — updates via the UI git pull.
70
71	  Clone source priority:
72	    1. Gitea (internal) — reads settings from varaverk.cfg; detects container IP at runtime
73	    2. GitHub (public)  — HTTPS fallback if Gitea is unreachable
74	  ────────────────────────────────────────────────────────────────────────────────
75	-->
76	<FILE Run="/bin/bash" Method="install">
77	<INLINE>
78	<![CDATA[
79	#!/bin/bash
80	PLUGIN="varaverk"
81	CFG_DIR="/boot/config/plugins/$PLUGIN"
82	CFG_FILE="$CFG_DIR/varaverk.cfg"
83	GITHUB="https://github.com/FailedProxy/Varaverk"
84	BRANCH="main"
85	LOG="$CFG_DIR/install.log"
86
87	mkdir -p "$CFG_DIR"
88	log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG"; }
89
90	# Scripts live in the plugin dir on flash — no array needed.
91	SCRIPTS_DIR="$CFG_DIR"
92	CONF_DIR="$SCRIPTS_DIR/Configurations"
93
94	# ── Boot device check ─────────────────────────────────────────────────────────
95	# Warn if /boot is on a USB/removable device. Varaverk is designed for internal
96	# NVMe/SSD boot — git repo + state files + data writes on USB will wear it out
97	# fast and may run out of space. Install proceeds but user is warned.
98	_boot_dev=$(df /boot --output=source 2>/dev/null | tail -1)
99	_boot_base=$(lsblk -no pkname "$_boot_dev" 2>/dev/null || basename "${_boot_dev%[0-9p]*}")
100	_removable=$(cat "/sys/block/${_boot_base}/removable" 2>/dev/null || echo "0")
101	if [[ "$_removable" == "1" ]]; then
102	    log "WARNING: /boot is on a removable/USB device ($_boot_dev)"
103	    log "WARNING: Varaverk is designed for internal NVMe/SSD boot."
104	    log "WARNING: Running from USB risks drive wear and space exhaustion."
105	    log "WARNING: Strongly recommend migrating boot to an internal NVMe/SSD drive."
106	fi
107	unset _boot_dev _boot_base _removable
108
109	# Seed varaverk.cfg with defaults (SCRIPTS_DIR + Gitea settings) if not present.
110	# Requires internal NVMe/SSD boot — scripts live on flash, available before array mounts.
111	if [[ ! -f "$CFG_FILE" ]]; then
112	    cat > "$CFG_FILE" <<'CFGEOF'
113	SCRIPTS_DIR="/boot/config/plugins/varaverk"
114	GITEA_CONTAINER="Gitea"
115	GITEA_REPO_PATH="FailedProxy/Varaverk.git"
116	GITEA_SSH_KEY="/root/.ssh/unraid_gitea"
117	SSH_PORT="221"
118	CFGEOF
119	    log "seeded varaverk.cfg"
120	fi
121
122	# Read Gitea settings from varaverk.cfg (allows override without editing .plg).
123	_read_cfg() { grep -oP "(?<=^${1}=\")[^\"]*" "$CFG_FILE" 2>/dev/null || echo "${2}"; }
124	GITEA_CONTAINER=$(_read_cfg GITEA_CONTAINER "Gitea")
125	GITEA_REPO_PATH=$(_read_cfg GITEA_REPO_PATH "FailedProxy/Varaverk.git")
126	GITEA_SSH_KEY=$(_read_cfg   GITEA_SSH_KEY   "/root/.ssh/unraid_gitea")
127	SSH_PORT=$(_read_cfg        SSH_PORT        "221")
128
129	# Clone on first install only; never auto-pull (updates via the UI git pull).
130	if [[ ! -d "$SCRIPTS_DIR/.git" ]]; then
131	    log "initialising repo in $SCRIPTS_DIR ($BRANCH)..."
132
133	    # Locate Gitea: local container → local IP; else Tailscale; else fall back to GitHub.
134	    GITEA_IP=""
135	    if command -v docker >/dev/null 2>&1 && \
136	       docker ps --format "{{.Names}}" 2>/dev/null | grep -q "^${GITEA_CONTAINER}$"; then
137	        GITEA_IP=$(hostname -I | awk '{print $1}')
138	        log "Gitea running locally — using $GITEA_IP"
139	    elif command -v tailscale >/dev/null 2>&1; then
140	        # Try each known peer until we find one hosting Gitea
141	        while IFS= read -r peer_ip; do
142	            if ssh -i "$GITEA_SSH_KEY" -p "$SSH_PORT" \
143	                   -o ConnectTimeout=3 -o StrictHostKeyChecking=no \
144	                   -o BatchMode=yes "git@${peer_ip}" info 2>/dev/null | grep -q "varaverk\|Gitea\|gitea"; then
145	                GITEA_IP="$peer_ip"
146	                log "Gitea found on Tailscale peer $GITEA_IP"
147	                break
148	            fi
149	        done < <(tailscale status --json 2>/dev/null | \
150	                 python3 -c "import json,sys; d=json.load(sys.stdin); \
151	                 [print(v['TailscaleIPs'][0]) for v in d.get('Peer',{}).values() \
152	                  if v.get('TailscaleIPs')]" 2>/dev/null)
153	    fi
154
155	    # init-in-place — git clone would fail because the dir already has files.
156	    git -C "$SCRIPTS_DIR" init >> "$LOG" 2>&1
157
158	    CLONED=false
159	    if [[ -n "$GITEA_IP" && -f "$GITEA_SSH_KEY" ]]; then
160	        GITEA_URL="ssh://git@${GITEA_IP}:${SSH_PORT}/${GITEA_REPO_PATH}"
161	        log "trying Gitea: $GITEA_URL"
162	        git -C "$SCRIPTS_DIR" remote add origin "$GITEA_URL" >> "$LOG" 2>&1
163	        if GIT_SSH_COMMAND="ssh -i $GITEA_SSH_KEY -p $SSH_PORT -o StrictHostKeyChecking=no" \
164	           GIT_TERMINAL_PROMPT=0 \
165	           git -C "$SCRIPTS_DIR" fetch --depth=1 origin "$BRANCH" >> "$LOG" 2>&1; then
166	            git -C "$SCRIPTS_DIR" reset --hard FETCH_HEAD >> "$LOG" 2>&1
167	            git -C "$SCRIPTS_DIR" branch -M "$BRANCH" >> "$LOG" 2>&1
168	            git -C "$SCRIPTS_DIR" branch --set-upstream-to=origin/"$BRANCH" "$BRANCH" >> "$LOG" 2>&1
169	            log "scripts installed from Gitea ($GITEA_IP)"
170	            CLONED=true
171	        else
172	            log "Gitea fetch failed — falling back to GitHub"
173	            git -C "$SCRIPTS_DIR" remote remove origin >> "$LOG" 2>&1 || true
174	        fi
175	    fi
176
177	    if [[ "$CLONED" == false ]]; then
178	        log "trying GitHub: $GITHUB"
179	        git -C "$SCRIPTS_DIR" remote add origin "$GITHUB" >> "$LOG" 2>&1
180	        if GIT_TERMINAL_PROMPT=0 \
181	           git -C "$SCRIPTS_DIR" fetch --depth=1 origin "$BRANCH" >> "$LOG" 2>&1; then
182	            git -C "$SCRIPTS_DIR" reset --hard FETCH_HEAD >> "$LOG" 2>&1
183	            git -C "$SCRIPTS_DIR" branch -M "$BRANCH" >> "$LOG" 2>&1
184	            git -C "$SCRIPTS_DIR" branch --set-upstream-to=origin/"$BRANCH" "$BRANCH" >> "$LOG" 2>&1
185	            log "scripts installed from GitHub"
186	            CLONED=true
187	        else
188	            log "WARNING: both Gitea and GitHub failed — scripts not installed, retry when network is up"
189	            exit 0
190	        fi
191	    fi
192	else
193	    log "repo present — leaving scripts untouched (update from the UI)"
194	fi
195
196	# Seed master.conf from template if absent.
197	mkdir -p "$CONF_DIR"
198	if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then
199	    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"
200	    log "seeded master.conf from template"
201	fi
202	log "install step complete"
203	]]>
204	</INLINE>
205	</FILE>
206
207	<!--
208	  ── 3. Remove ───────────────────────────────────────────────────────────────────
209	  Stops background scripts, removes cron, the installed package, and flash config.
210	  Scripts/conf in appdata are left intact (delete manually for a full wipe).
211	  ────────────────────────────────────────────────────────────────────────────────
212	-->
213	<FILE Run="/bin/bash" Method="remove">
214	<INLINE>
215	<![CDATA[
216	#!/bin/bash
217	PLUGIN="varaverk"
218	CFG_DIR="/boot/config/plugins/$PLUGIN"
219	CFG_FILE="$CFG_DIR/varaverk.cfg"
220	CRON_FILE="$CFG_DIR/varaverk.cron"
221	WEB_DIR="/usr/local/emhttp/plugins/$PLUGIN"
222	log() { echo "[Varaverk remove] $*"; }
223
224	[[ -f "$CFG_FILE" ]] && _sd=$(grep -oP '(?<=SCRIPTS_DIR=")[^"]+' "$CFG_FILE" 2>/dev/null)
225	SCRIPTS_DIR="${_sd:-$CFG_DIR}"
226
227	# Stop continuous background scripts.
228	if [[ -f "$SCRIPTS_DIR/Fallback/fallback.sh" ]]; then
229	    bash "$SCRIPTS_DIR/Fallback/fallback.sh" --stop 2>/dev/null && log "fallback.sh stopped" || true
230	fi
231	pkill -f "run_job.sh" 2>/dev/null || true
232	pkill -f "watchdog_orchestrator.sh" 2>/dev/null || true
233
234	# Remove cron entries.
235	if [[ -f "$CRON_FILE" ]]; then
236	    rm -f "$CRON_FILE"
237	    /usr/local/sbin/update_cron 2>/dev/null || true
238	    log "cron removed"
239	fi
240	rm -f /etc/cron.d/varaverk
241
242	# Remove the installed package (and its RAM files).
243	removepkg "$PLUGIN" 2>/dev/null || true
244	[[ -L "$WEB_DIR" ]] && rm -f "$WEB_DIR"
245	[[ -d "$WEB_DIR" ]] && rm -rf "$WEB_DIR"
246	log "web files removed"
247
248	# Remove flash config (incl. cached .txz).
249	rm -rf "$CFG_DIR"
250	log "flash config removed"
251	log "done — scripts/conf in $SCRIPTS_DIR preserved (delete manually for full wipe)"
252	]]>
253	</INLINE>
254	</FILE>
255
256	</PLUGIN>
257

<?xml version='1.0' standalone='yes'?>
<!DOCTYPE PLUGIN [
<!ENTITY name    "varaverk">
<!ENTITY author  "gmer4lfe">
<!ENTITY version "2026.05.31">
<!ENTITY sha256  "d588470d6cc7f284601cb56039d5dfea6fb5bb1ee2c800657438a8edacafbd01">
<!ENTITY launch  "varaverk/monitor">
<!ENTITY github  "https://github.com/FailedProxy/Varaverk">
<!ENTITY branch  "main">
<!ENTITY cfgdir  "/boot/config/plugins/varaverk">
<!ENTITY plugdir "/usr/local/emhttp/plugins/varaverk">
<!ENTITY pkg     "varaverk-&version;-noarch-1.txz">
]>
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;"
        support="https://github.com/FailedProxy/Varaverk/issues"
        icon="/plugins/varaverk/icons/varaverk.png">

<CHANGES>
###2026.05.31
- Packaged release: web files now ship as a .txz that Unraid reinstalls to RAM on every boot
- Survives reboots with zero manual steps (no symlink, no go script) — fixes plugin vanishing after OS upgrades
- Scripts are git-cloned to appdata on first install; web files stay on flash (~200KB)
- Updates handled in-UI (git pull); the plugin no longer pulls on every boot

###2026.05.30
- First-run setup wizard: auto-detects hostname, creates master.conf + host conf from templates
- Scheduler setup mode: after wizard, master.conf and host conf open sequentially with forced save flow
- Partnership tab: Onboard button highlighted on arrival from wizard; disabled until partner is configured
- HOST2 install paths: state-file pull, master.conf push detection, conf-only flow
- Onboard Step 9: master.conf automatically pushed to all listed hosts on onboard completion
- Graceful pre-onboard state: neutral banners instead of error warnings before SSH is configured
- GitHub link in tab bar and Settings page; Community Apps support URL

###2026.05.28
- Initial release: Monitor, Scheduler, Docker, Watchdog, Partnership, Fallback, Arrs tabs
- Mutual container fallback with tiered escalation and strike-confirmed handback
- Partnership lifecycle: onboard, offboard, transfer
- Rsync profile system with per-share container stops and writeback
- Watchdog: Tier 1 (explicit) + Tier 2 (global scan) container monitoring
- master.conf push-on-save to all configured partners via SSH
</CHANGES>

<!--
  ── 1. Web files symlink (runs on every boot) ──────────────────────────────────
  Instead of extracting a .txz, we symlink the installed plugin web dir directly
  to the workspace on flash. Changes to Plugin/unraid/ are live instantly — no
  package build, no sync step. /boot is always mounted before this runs.
  ────────────────────────────────────────────────────────────────────────────────
-->
<FILE Run="/bin/bash">
<INLINE>
<![CDATA[
#!/bin/bash
WEB_DIR="/usr/local/emhttp/plugins/varaverk"
SRC="/boot/config/plugins/varaverk/Plugin/unraid"
[[ -L "$WEB_DIR" ]] && rm -f "$WEB_DIR"
[[ -d "$WEB_DIR" ]] && rm -rf "$WEB_DIR"
ln -sf "$SRC" "$WEB_DIR"
echo "[Varaverk] web dir symlinked → $SRC"
]]>
</INLINE>
</FILE>

<!--
  ── 2. Scripts bootstrap (first install only) ──────────────────────────────────
  Clones the repo directly into the plugin config dir on flash (/boot/config/plugins/varaverk).
  No array dependency — scripts live on flash (64GB NVMe) and are available at boot.
  Uses git init+fetch+reset so the clone works into the non-empty cfgdir (varaverk.cfg,
  varaverk-*.txz etc. are already there). Never auto-pulls — updates via the UI git pull.

  Clone source priority:
    1. Gitea (internal) — reads settings from varaverk.cfg; detects container IP at runtime
    2. GitHub (public)  — HTTPS fallback if Gitea is unreachable
  ────────────────────────────────────────────────────────────────────────────────
-->
<FILE Run="/bin/bash" Method="install">
<INLINE>
<![CDATA[
#!/bin/bash
PLUGIN="varaverk"
CFG_DIR="/boot/config/plugins/$PLUGIN"
CFG_FILE="$CFG_DIR/varaverk.cfg"
GITHUB="https://github.com/FailedProxy/Varaverk"
BRANCH="main"
LOG="$CFG_DIR/install.log"

mkdir -p "$CFG_DIR"
log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG"; }

# Scripts live in the plugin dir on flash — no array needed.
SCRIPTS_DIR="$CFG_DIR"
CONF_DIR="$SCRIPTS_DIR/Configurations"

# ── Boot device check ─────────────────────────────────────────────────────────
# Warn if /boot is on a USB/removable device. Varaverk is designed for internal
# NVMe/SSD boot — git repo + state files + data writes on USB will wear it out
# fast and may run out of space. Install proceeds but user is warned.
_boot_dev=$(df /boot --output=source 2>/dev/null | tail -1)
_boot_base=$(lsblk -no pkname "$_boot_dev" 2>/dev/null || basename "${_boot_dev%[0-9p]*}")
_removable=$(cat "/sys/block/${_boot_base}/removable" 2>/dev/null || echo "0")
if [[ "$_removable" == "1" ]]; then
    log "WARNING: /boot is on a removable/USB device ($_boot_dev)"
    log "WARNING: Varaverk is designed for internal NVMe/SSD boot."
    log "WARNING: Running from USB risks drive wear and space exhaustion."
    log "WARNING: Strongly recommend migrating boot to an internal NVMe/SSD drive."
fi
unset _boot_dev _boot_base _removable

# Seed varaverk.cfg with defaults (SCRIPTS_DIR + Gitea settings) if not present.
# Requires internal NVMe/SSD boot — scripts live on flash, available before array mounts.
if [[ ! -f "$CFG_FILE" ]]; then
    cat > "$CFG_FILE" <<'CFGEOF'
SCRIPTS_DIR="/boot/config/plugins/varaverk"
GITEA_CONTAINER="Gitea"
GITEA_REPO_PATH="FailedProxy/Varaverk.git"
GITEA_SSH_KEY="/root/.ssh/unraid_gitea"
SSH_PORT="221"
CFGEOF
    log "seeded varaverk.cfg"
fi

# Read Gitea settings from varaverk.cfg (allows override without editing .plg).
_read_cfg() { grep -oP "(?<=^${1}=\")[^\"]*" "$CFG_FILE" 2>/dev/null || echo "${2}"; }
GITEA_CONTAINER=$(_read_cfg GITEA_CONTAINER "Gitea")
GITEA_REPO_PATH=$(_read_cfg GITEA_REPO_PATH "FailedProxy/Varaverk.git")
GITEA_SSH_KEY=$(_read_cfg   GITEA_SSH_KEY   "/root/.ssh/unraid_gitea")
SSH_PORT=$(_read_cfg        SSH_PORT        "221")

# Clone on first install only; never auto-pull (updates via the UI git pull).
if [[ ! -d "$SCRIPTS_DIR/.git" ]]; then
    log "initialising repo in $SCRIPTS_DIR ($BRANCH)..."

    # Locate Gitea: local container → local IP; else Tailscale; else fall back to GitHub.
    GITEA_IP=""
    if command -v docker >/dev/null 2>&1 && \
       docker ps --format "{{.Names}}" 2>/dev/null | grep -q "^${GITEA_CONTAINER}$"; then
        GITEA_IP=$(hostname -I | awk '{print $1}')
        log "Gitea running locally — using $GITEA_IP"
    elif command -v tailscale >/dev/null 2>&1; then
        # Try each known peer until we find one hosting Gitea
        while IFS= read -r peer_ip; do
            if ssh -i "$GITEA_SSH_KEY" -p "$SSH_PORT" \
                   -o ConnectTimeout=3 -o StrictHostKeyChecking=no \
                   -o BatchMode=yes "git@${peer_ip}" info 2>/dev/null | grep -q "varaverk\|Gitea\|gitea"; then
                GITEA_IP="$peer_ip"
                log "Gitea found on Tailscale peer $GITEA_IP"
                break
            fi
        done < <(tailscale status --json 2>/dev/null | \
                 python3 -c "import json,sys; d=json.load(sys.stdin); \
                 [print(v['TailscaleIPs'][0]) for v in d.get('Peer',{}).values() \
                  if v.get('TailscaleIPs')]" 2>/dev/null)
    fi

    # init-in-place — git clone would fail because the dir already has files.
    git -C "$SCRIPTS_DIR" init >> "$LOG" 2>&1

    CLONED=false
    if [[ -n "$GITEA_IP" && -f "$GITEA_SSH_KEY" ]]; then
        GITEA_URL="ssh://git@${GITEA_IP}:${SSH_PORT}/${GITEA_REPO_PATH}"
        log "trying Gitea: $GITEA_URL"
        git -C "$SCRIPTS_DIR" remote add origin "$GITEA_URL" >> "$LOG" 2>&1
        if GIT_SSH_COMMAND="ssh -i $GITEA_SSH_KEY -p $SSH_PORT -o StrictHostKeyChecking=no" \
           GIT_TERMINAL_PROMPT=0 \
           git -C "$SCRIPTS_DIR" fetch --depth=1 origin "$BRANCH" >> "$LOG" 2>&1; then
            git -C "$SCRIPTS_DIR" reset --hard FETCH_HEAD >> "$LOG" 2>&1
            git -C "$SCRIPTS_DIR" branch -M "$BRANCH" >> "$LOG" 2>&1
            git -C "$SCRIPTS_DIR" branch --set-upstream-to=origin/"$BRANCH" "$BRANCH" >> "$LOG" 2>&1
            log "scripts installed from Gitea ($GITEA_IP)"
            CLONED=true
        else
            log "Gitea fetch failed — falling back to GitHub"
            git -C "$SCRIPTS_DIR" remote remove origin >> "$LOG" 2>&1 || true
        fi
    fi

    if [[ "$CLONED" == false ]]; then
        log "trying GitHub: $GITHUB"
        git -C "$SCRIPTS_DIR" remote add origin "$GITHUB" >> "$LOG" 2>&1
        if GIT_TERMINAL_PROMPT=0 \
           git -C "$SCRIPTS_DIR" fetch --depth=1 origin "$BRANCH" >> "$LOG" 2>&1; then
            git -C "$SCRIPTS_DIR" reset --hard FETCH_HEAD >> "$LOG" 2>&1
            git -C "$SCRIPTS_DIR" branch -M "$BRANCH" >> "$LOG" 2>&1
            git -C "$SCRIPTS_DIR" branch --set-upstream-to=origin/"$BRANCH" "$BRANCH" >> "$LOG" 2>&1
            log "scripts installed from GitHub"
            CLONED=true
        else
            log "WARNING: both Gitea and GitHub failed — scripts not installed, retry when network is up"
            exit 0
        fi
    fi
else
    log "repo present — leaving scripts untouched (update from the UI)"
fi

# Seed master.conf from template if absent.
mkdir -p "$CONF_DIR"
if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then
    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"
    log "seeded master.conf from template"
fi
log "install step complete"
]]>
</INLINE>
</FILE>

<!--
  ── 3. Remove ───────────────────────────────────────────────────────────────────
  Stops background scripts, removes cron, the installed package, and flash config.
  Scripts/conf in appdata are left intact (delete manually for a full wipe).
  ────────────────────────────────────────────────────────────────────────────────
-->
<FILE Run="/bin/bash" Method="remove">
<INLINE>
<![CDATA[
#!/bin/bash
PLUGIN="varaverk"
CFG_DIR="/boot/config/plugins/$PLUGIN"
CFG_FILE="$CFG_DIR/varaverk.cfg"
CRON_FILE="$CFG_DIR/varaverk.cron"
WEB_DIR="/usr/local/emhttp/plugins/$PLUGIN"
log() { echo "[Varaverk remove] $*"; }

[[ -f "$CFG_FILE" ]] && _sd=$(grep -oP '(?<=SCRIPTS_DIR=")[^"]+' "$CFG_FILE" 2>/dev/null)
SCRIPTS_DIR="${_sd:-$CFG_DIR}"

# Stop continuous background scripts.
if [[ -f "$SCRIPTS_DIR/Fallback/fallback.sh" ]]; then
    bash "$SCRIPTS_DIR/Fallback/fallback.sh" --stop 2>/dev/null && log "fallback.sh stopped" || true
fi
pkill -f "run_job.sh" 2>/dev/null || true
pkill -f "watchdog_orchestrator.sh" 2>/dev/null || true

# Remove cron entries.
if [[ -f "$CRON_FILE" ]]; then
    rm -f "$CRON_FILE"
    /usr/local/sbin/update_cron 2>/dev/null || true
    log "cron removed"
fi
rm -f /etc/cron.d/varaverk

# Remove the installed package (and its RAM files).
removepkg "$PLUGIN" 2>/dev/null || true
[[ -L "$WEB_DIR" ]] && rm -f "$WEB_DIR"
[[ -d "$WEB_DIR" ]] && rm -rf "$WEB_DIR"
log "web files removed"

# Remove flash config (incl. cached .txz).
rm -rf "$CFG_DIR"
log "flash config removed"
log "done — scripts/conf in $SCRIPTS_DIR preserved (delete manually for full wipe)"
]]>
</INLINE>
</FILE>

</PLUGIN>


so host1 api is filled in, the accual template is located in Configurations/host.conf.template. so we need to point it there or move the config folder to the deployment page, but at the same time that…


Review onboarding and first launch setup


Found the important thing. The `.plg` on first install seeds master.conf from `Deployment/conf_templates/master.conf` — not from `Configurations/`. Let me check what's in that template vs the real one, and trace the full HOST2 first-install path.


Bash

grep -n "HOST1\|HOST2\|=.*\"\"" /boot/config/plugins/varaverk/Deployment/conf_templates/master.conf | head -20

Check HOST1/HOST2 values in master.conf template


11:#   source host1.conf    ← HOST1 credentials, shares, container lists
12:#   source host2.conf    ← HOST2 credentials, shares, container lists
15:# HOST2 never sees HOST1 credentials. HOST1 never sees HOST2 credentials.
108:    HOST1="unRAID-Gmer4Lfe"
109:    HOST2="unRAID-Jayred365"
154:# HOST1 is always the owner (source of truth) — HOST2 is always the mirror.
155:# PARTNERSHIP_OWNER_HOST flips to "HOST2" after a --transfer operation.
160:#   STATE_DIR/partnership_HOST1.db ← HOST1 writes only
161:#   STATE_DIR/partnership_HOST2.db ← HOST2 writes only
175:    PARTNERSHIP_OWNER_HOST="HOST1"          # "HOST1" or "HOST2" — flips on --transfer
179:# HOST1_PARTNERSHIP_AUTH_WEBUIS / HOST2_PARTNERSHIP_AUTH_WEBUIS
184:# HOST1_PARTNERSHIP_MIRROR_BACKUPS / HOST2_PARTNERSHIP_MIRROR_BACKUPS
206:    TAILSCALE_API_KEY=""                    # tskey-api-...
207:    TAILSCALE_TAILNET=""                    # your tailnet name (e.g. yourname.github)
231:    PARTNERSHIP_FOLDERVIEW3_URL=""          # CA plugin URL — leave empty to skip auto-install
251:# HOST1_DISCORD_WEBHOOK / HOST2_DISCORD_WEBHOOK
263:    GITEA_DOMAIN=""                         # e.g. git.yourdomain.com — requires NPM + DNS
336:# HOST1_CRITICAL_SYNC_SHARES / HOST2_CRITICAL_SYNC_SHARES
364:        "Media/lidarr_missing_art.sh"                 # fetch missing album/artist artwork (HOST1 only — self-guards)
377:# Defined per-host in host*.conf — HOST1_DAILY_SYNC_SHARES and HOST2_DAILY_SYNC_SHARES.


Now I have the full picture. Here's the accurate summary:

---

## HOST2 Install Path (What Actually Happens)

The `.plg` on HOST2:
1. Symlinks `Plugin/unraid` → the web server dir (every boot)
2. On first install: git clones the full repo into `/boot/config/plugins/varaverk/`
3. Seeds master.conf from `Deployment/conf_templates/master.conf` **only if master.conf doesn't exist** — but since git clone already pulled `Configurations/master.conf`, this seed never runs in practice

After the clone, HOST2 has:
- `Configurations/master.conf` — `HOST1="unRAID-Gmer4Lfe"` already set ✓
- `Configurations/host2.conf` — exists (sparse checkout is `/*`, gets everything)
- `Configurations/host.conf.template` — correct HOSTN template ✓

**The wizard will NOT trigger** — both first-run conditions are false:
- `HOST1` is not blank in master.conf
- `host2.conf` exists

HOST2 goes straight to the normal plugin UI.

---

## What HOST2 Still Needs After Install

These need to happen manually or via the existing UI:

| What | How | State in host2.conf |
|------|-----|---------------------|
| SSH key | Partnership tab → generate | path set, file missing |
| Emby API key | Fill in manually | placeholder string |
| Arr keys (Sonarr/Radarr) | `conf_populate.sh` auto-detects | already filled from before |
| NPM/lldap creds | Fill in manually | blank |
| Discord webhook | Fill in manually | blank |
| Weekly/critical sync shares | Uncomment in host2.conf | all commented out |

---

## The One Actual Issue

`Deployment/conf_templates/master.conf` has the real hostnames hardcoded (`HOST1="unRAID-Gmer4Lfe"`). The intent was a blank seed template for brand-new installs (git failure case), but it would also skip the wizard for someone installing fresh from GitHub with no prior setup. Low risk for this use case since it's a private setup, but worth knowing.

---

**Bottom line**: HOST2 setup is ready to test. The wizard is intentionally bypassed (confs already exist). The first things to do on HOST2 after install are: Partnership tab → generate SSH key → authorize it on HOST1 → run `conf_populate.sh` → fill in remaining blanks. Want me to clean up the stale `Deployment/conf_templates/host.conf.template` while we're here?


We were reviewing the onboarding and first-launch setup before testing on HOST2. Everything looks ready — the next step is installing the plugin on HOST2 and walking through the post-install sequence: Partnership tab → generate SSH key → authorize on HOST1 → run conf_populate.sh. (disable recaps in /config)


well i want both .confs in the same place so either we keep the correct copies in deployment or in config, but the 2 stale need to go... all.conf in one place, then update scripts to point to the correct path


Let me map exactly what exists and what references each file before touching anything.


ls -la /boot/config/plugins/varaverk/Configurations/ && echo "---" && ls -la /boot/config/plugins/varaverk/Deployment/conf_templates/

List both template locations with details


total 229
drwxrwxrwx  2 root root     9 Jun 12 21:30 ./
drwxrwxrwx 23 root root    38 Jun  7 10:12 ../
-rw-r--r--  1 root root 22192 Jun  4 01:00 host.conf.template
-rw-r--r--  1 root root 41475 Jun 12 21:30 host1.conf
-rw-rw-rw-  1 root root 41475 Jun 12 01:00 host1.conf.bak
-rw-rw-rw-  1 root root 35169 Jun  5 18:43 host2.conf
-rw-rw-rw-  1 root root 92778 Jun  7 10:11 master.conf
-rw-rw-rw-  1 root root 92902 Jun  7 01:00 master.conf.bak
-rw-r--r--  1 root root 91284 Jun  4 01:00 master.conf.template
---
total 62
drwxrwxrwx 2 root root     4 Jun  5 20:22 ./
drwxrwxrwx 3 root root     5 May 31 13:17 ../
-rw-r--r-- 1 root root 41313 Jun  4 01:00 host.conf.template
-rw-rw-rw- 1 root root 91829 Jun  5 20:22 master.conf


grep -rn "conf_templates\|host\.conf\.template\|master\.conf\.template" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" --include="*.plg" 2>/dev/null | grep -v ".git/"

Find all references to template paths across scripts and plugin files


/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh:56:# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --dry-run
/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh:59:# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --backup
/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh:62:# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf
/boot/config/plugins/varaverk/Plugin/varaverk.plg:198:if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then
/boot/config/plugins/varaverk/Plugin/varaverk.plg:199:    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"
/boot/config/plugins/varaverk/git_pull_execute.sh:284:TMPL_DIR="$TARGET_DIR/Deployment/conf_templates"
/boot/config/plugins/varaverk/git_pull_execute.sh:306:    if [[ -f "$TMPL_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then
/boot/config/plugins/varaverk/git_pull_execute.sh:308:            --template "$TMPL_DIR/host.conf.template" \
/boot/config/plugins/varaverk/git_pull_execute.sh:312:        warn "${MY_ID,,}.conf or host.conf.template not found — skipping"
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:140:        $template = @file_get_contents(CONF_DIR . '/host.conf.template') ?: '';
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:219:    $template = @file_get_contents(CONF_DIR . '/host.conf.template') ?: '';


well i want both .confs in the same place so either we keep the correct copies in deployment or in config, but the 2 stale need to go... all.conf in one place, then update scripts to point to the corr…


Now I have the complete map. Let me check git_pull_execute.sh around the template reference and verify the two master.conf templates are in sync.


275	# Merges new conf structure into the live conf files after every pull.
276	# New keys → added with template defaults (user fills in once).
277	# Removed keys → dropped. Existing values → always preserved.
278	# Silent when already up to date — no overhead on unchanged pulls.
279
280	echo ""
281	echo "━━━ $ICON_GEAR Conf Upgrade ━━━"
282
283	UPGRADE_SCRIPT="$TARGET_DIR/Deployment/conf_upgrade.sh"
284	TMPL_DIR="$TARGET_DIR/Deployment/conf_templates"
285	CONF_DIR="$TARGET_DIR/Configurations"
286
287	if [[ ! -f "$UPGRADE_SCRIPT" ]]; then
288	    log "conf_upgrade.sh not found — skipping (pre-deployment-folder repo)"
289	elif [[ "$DRY_RUN" == true ]]; then
290	    warn "DRY RUN — would upgrade master.conf and ${MY_ID,,}.conf"
291	elif [[ "$SYNC_SUCCESS" == true ]]; then
292	    _DRY=""
293
294	    # master.conf
295	    if [[ -f "$TMPL_DIR/master.conf" && -f "$CONF_DIR/master.conf" ]]; then
296	        bash "$UPGRADE_SCRIPT" \
297	            --template "$TMPL_DIR/master.conf" \
298	            --target   "$CONF_DIR/master.conf" \
299	            --backup   $_DRY
300	    else
301	        warn "master.conf template or target not found — skipping"
302	    fi
303
304	    # This server's host conf only — sparse checkout ensures we have it
305	    HOST_CONF="$CONF_DIR/${MY_ID,,}.conf"
306	    if [[ -f "$TMPL_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then
307	        bash "$UPGRADE_SCRIPT" \
308	            --template "$TMPL_DIR/host.conf.template" \
309	            --target   "$HOST_CONF" \
310	            --backup   $_DRY
311	    else
312	        warn "${MY_ID,,}.conf or host.conf.template not found — skipping"
313	    fi
314	fi
315
316	# ==============================================================================================
317	# ━━━ Summary ━━━
318	# ==============================================================================================
319	echo ""
320	echo "━━━━━ $ICON_SUMMARY GIT SYNC SUMMARY ━━━━━"
321	echo "$ICON_NET  Repo:     $REPO_SSH"
322	echo "$ICON_GEAR Target:   $TARGET_DIR"
323	echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
324	echo "$ICON_LOCK Excluded: peer host*.conf files"

# Merges new conf structure into the live conf files after every pull.
# New keys → added with template defaults (user fills in once).
# Removed keys → dropped. Existing values → always preserved.
# Silent when already up to date — no overhead on unchanged pulls.

echo ""
echo "━━━ $ICON_GEAR Conf Upgrade ━━━"

UPGRADE_SCRIPT="$TARGET_DIR/Deployment/conf_upgrade.sh"
TMPL_DIR="$TARGET_DIR/Deployment/conf_templates"
CONF_DIR="$TARGET_DIR/Configurations"

if [[ ! -f "$UPGRADE_SCRIPT" ]]; then
    log "conf_upgrade.sh not found — skipping (pre-deployment-folder repo)"
elif [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would upgrade master.conf and ${MY_ID,,}.conf"
elif [[ "$SYNC_SUCCESS" == true ]]; then
    _DRY=""

    # master.conf
    if [[ -f "$TMPL_DIR/master.conf" && -f "$CONF_DIR/master.conf" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$TMPL_DIR/master.conf" \
            --target   "$CONF_DIR/master.conf" \
            --backup   $_DRY
    else
        warn "master.conf template or target not found — skipping"
    fi

    # This server's host conf only — sparse checkout ensures we have it
    HOST_CONF="$CONF_DIR/${MY_ID,,}.conf"
    if [[ -f "$TMPL_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$TMPL_DIR/host.conf.template" \
            --target   "$HOST_CONF" \
            --backup   $_DRY
    else
        warn "${MY_ID,,}.conf or host.conf.template not found — skipping"
    fi
fi

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY GIT SYNC SUMMARY ━━━━━"
echo "$ICON_NET  Repo:     $REPO_SSH"
echo "$ICON_GEAR Target:   $TARGET_DIR"
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_LOCK Excluded: peer host*.conf files"


diff /boot/config/plugins/varaverk/Configurations/master.conf.template /boot/config/plugins/varaverk/Deployment/conf_templates/master.conf | head -40

Diff the two master.conf template files


108,109c108,109
<     HOST1=""    # this server's Unraid hostname — must match exactly (case-sensitive)
<     HOST2=""    # partner server hostname — leave blank if running standalone
---
>     HOST1="unRAID-Gmer4Lfe"
>     HOST2="unRAID-Jayred365"
114,118c114,122
< # DATA_DIR is the same path on all servers — persistent script state and statistics.
< # Array share — survives reboots, no flash drive wear.
< # Created automatically if it doesn't exist.
< # Only truly critical files (fallback state, watchdog reboot log) stay on /boot/config.
<     DATA_DIR="/mnt/user/appdata/Varaverk/data"
---
> # Requirement: internal NVMe/SSD boot drive. Everything lives in the plugin folder on flash —
> # 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"
155,157c159,163
< # State files on /boot/config — survives reboots, available before array starts:
< #   /boot/config/partnership_HOST1.db ← HOST1 writes only
< #   /boot/config/partnership_HOST2.db ← HOST2 writes only
---
> # State files in STATE_DIR — survives reboots, array must be up (scripts run after array start):
> #   STATE_DIR/partnership_HOST1.db ← HOST1 writes only
> #   STATE_DIR/partnership_HOST2.db ← HOST2 writes only
> #   STATE_DIR/partnership_blocklist.db ← shared blocklist
> #   STATE_DIR/varaverk_setup.db ← onboarding progress state
187a194,198
> # Partnership and setup state files — all in STATE_DIR per the project requirement.
> # Scripts use these variables; do not hardcode /boot/config paths in scripts.
>     PARTNERSHIP_BLOCKLIST_FILE="$STATE_DIR/partnership_blocklist.db"
>     VARAVERK_SETUP_FILE="$STATE_DIR/varaverk_setup.db"
>
251c262
<     GITEA_REPO_PATH=""                  # e.g. YourUser/Varaverk.git


Clear picture. `Configurations/master.conf.template` is the up-to-date one (HOST1/HOST2 blank, correct DATA_DIR/STATE_DIR paths). `Deployment/conf_templates/master.conf` is stale with wrong appdata paths and hardcoded hostnames.

**Plan:**
- Everything lives in `Configurations/` — templates already have the right names there (`host.conf.template`, `master.conf.template`)
- Delete both stale files from `Deployment/conf_templates/`, remove that directory
- Update `.plg` to seed from `Configurations/master.conf.template`
- Update `git_pull_execute.sh` to pull templates from `Configurations/`
- Update `conf_upgrade.sh` example comments


rm /boot/config/plugins/varaverk/Deployment/conf_templates/host.conf.template \
   /boot/config/plugins/varaverk/Deployment/conf_templates/master.conf && \
rmdir /boot/config/plugins/varaverk/Deployment/conf_templates && \
echo "done"

Delete stale template files and now-empty conf_templates directory


done


Now fix the `.plg`:


196	# Seed master.conf from template if absent.
197	mkdir -p "$CONF_DIR"
198	if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then
199	    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"
200	    log "seeded master.conf from template"
201	fi

# Seed master.conf from template if absent.
mkdir -p "$CONF_DIR"
if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then
    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"
    log "seeded master.conf from template"
fi


Edit

if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then
    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"

if [[ ! -f "$CONF_DIR/master.conf" && -f "$CONF_DIR/master.conf.template" ]]; then
    cp "$CONF_DIR/master.conf.template" "$CONF_DIR/master.conf"


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

if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then
    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"

if [[ ! -f "$CONF_DIR/master.conf" && -f "$CONF_DIR/master.conf.template" ]]; then
    cp "$CONF_DIR/master.conf.template" "$CONF_DIR/master.conf"

# Seed master.conf from template if absent.

-if [[ ! -f "$CONF_DIR/master.conf" && -f "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" ]]; then

-    cp "$SCRIPTS_DIR/Deployment/conf_templates/master.conf" "$CONF_DIR/master.conf"

+if [[ ! -f "$CONF_DIR/master.conf" && -f "$CONF_DIR/master.conf.template" ]]; then

+    cp "$CONF_DIR/master.conf.template" "$CONF_DIR/master.conf"


Now fix `git_pull_execute.sh`:


UPGRADE_SCRIPT="$TARGET_DIR/Deployment/conf_upgrade.sh"
TMPL_DIR="$TARGET_DIR/Deployment/conf_templates"
CONF_DIR="$TARGET_DIR/Configurations"

if [[ ! -f "$UPGRADE_SCRIPT" ]]; then
    log "conf_upgrade.sh not found — skipping (pre-deployment-folder repo)"
elif [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would upgrade master.conf and ${MY_ID,,}.conf"
elif [[ "$SYNC_SUCCESS" == true ]]; then
    _DRY=""

    # master.conf
    if [[ -f "$TMPL_DIR/master.conf" && -f "$CONF_DIR/master.conf" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$TMPL_DIR/master.conf" \
            --target   "$CONF_DIR/master.conf" \
            --backup   $_DRY
    else
        warn "master.conf template or target not found — skipping"
    fi

    # This server's host conf only — sparse checkout ensures we have it
    HOST_CONF="$CONF_DIR/${MY_ID,,}.conf"
    if [[ -f "$TMPL_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$TMPL_DIR/host.conf.template" \
            --target   "$HOST_CONF" \
            --backup   $_DRY
    else
        warn "${MY_ID,,}.conf or host.conf.template not found — skipping"
    fi
fi

UPGRADE_SCRIPT="$TARGET_DIR/Deployment/conf_upgrade.sh"
CONF_DIR="$TARGET_DIR/Configurations"

if [[ ! -f "$UPGRADE_SCRIPT" ]]; then
    log "conf_upgrade.sh not found — skipping (pre-deployment-folder repo)"
elif [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would upgrade master.conf and ${MY_ID,,}.conf"
elif [[ "$SYNC_SUCCESS" == true ]]; then
    _DRY=""

    # master.conf
    if [[ -f "$CONF_DIR/master.conf.template" && -f "$CONF_DIR/master.conf" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$CONF_DIR/master.conf.template" \
            --target   "$CONF_DIR/master.conf" \
            --backup   $_DRY
    else
        warn "master.conf.template or master.conf not found — skipping"
    fi

    # This server's host conf only — sparse checkout ensures we have it
    HOST_CONF="$CONF_DIR/${MY_ID,,}.conf"
    if [[ -f "$CONF_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$CONF_DIR/host.conf.template" \
            --target   "$HOST_CONF" \
            --backup   $_DRY
    else
        warn "${MY_ID,,}.conf or host.conf.template not found — skipping"
    fi
fi


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

UPGRADE_SCRIPT="$TARGET_DIR/Deployment/conf_upgrade.sh"
TMPL_DIR="$TARGET_DIR/Deployment/conf_templates"
CONF_DIR="$TARGET_DIR/Configurations"

if [[ ! -f "$UPGRADE_SCRIPT" ]]; then
    log "conf_upgrade.sh not found — skipping (pre-deployment-folder repo)"
elif [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would upgrade master.conf and ${MY_ID,,}.conf"
elif [[ "$SYNC_SUCCESS" == true ]]; then
    _DRY=""

    # master.conf
    if [[ -f "$TMPL_DIR/master.conf" && -f "$CONF_DIR/master.conf" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$TMPL_DIR/master.conf" \
            --target   "$CONF_DIR/master.conf" \
            --backup   $_DRY
    else
        warn "master.conf template or target not found — skipping"
    fi

    # This server's host conf only — sparse checkout ensures we have it
    HOST_CONF="$CONF_DIR/${MY_ID,,}.conf"
    if [[ -f "$TMPL_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$TMPL_DIR/host.conf.template" \
            --target   "$HOST_CONF" \
            --backup   $_DRY
    else
        warn "${MY_ID,,}.conf or host.conf.template not found — skipping"
    fi
fi

UPGRADE_SCRIPT="$TARGET_DIR/Deployment/conf_upgrade.sh"
CONF_DIR="$TARGET_DIR/Configurations"

if [[ ! -f "$UPGRADE_SCRIPT" ]]; then
    log "conf_upgrade.sh not found — skipping (pre-deployment-folder repo)"
elif [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would upgrade master.conf and ${MY_ID,,}.conf"
elif [[ "$SYNC_SUCCESS" == true ]]; then
    _DRY=""

    # master.conf
    if [[ -f "$CONF_DIR/master.conf.template" && -f "$CONF_DIR/master.conf" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$CONF_DIR/master.conf.template" \
            --target   "$CONF_DIR/master.conf" \
            --backup   $_DRY
    else
        warn "master.conf.template or master.conf not found — skipping"
    fi

    # This server's host conf only — sparse checkout ensures we have it
    HOST_CONF="$CONF_DIR/${MY_ID,,}.conf"
    if [[ -f "$CONF_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then
        bash "$UPGRADE_SCRIPT" \
            --template "$CONF_DIR/host.conf.template" \
            --target   "$HOST_CONF" \
            --backup   $_DRY
    else
        warn "${MY_ID,,}.conf or host.conf.template not found — skipping"
    fi
fi

-    if [[ -f "$TMPL_DIR/master.conf" && -f "$CONF_DIR/master.conf" ]]; then

+    if [[ -f "$CONF_DIR/master.conf.template" && -f "$CONF_DIR/master.conf" ]]; then

-            --template "$TMPL_DIR/master.conf" \

+            --template "$CONF_DIR/master.conf.template" \

-        warn "master.conf template or target not found — skipping"

+        warn "master.conf.template or master.conf not found — skipping"

-    if [[ -f "$TMPL_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then

+    if [[ -f "$CONF_DIR/host.conf.template" && -f "$HOST_CONF" ]]; then

-            --template "$TMPL_DIR/host.conf.template" \

+            --template "$CONF_DIR/host.conf.template" \


Now fix the example paths in `conf_upgrade.sh` comments:


# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --dry-run
#     Preview what would be added, removed, and kept — no changes written.
#
# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --backup
#     Apply the upgrade, writing a .bak first.
#
# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf
#     Apply the upgrade in-place with no backup.

# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --dry-run
#     Preview what would be added, removed, and kept — no changes written.
#
# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --backup
#     Apply the upgrade, writing a .bak first.
#
# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf
#     Apply the upgrade in-place with no backup.


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

# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --dry-run
#     Preview what would be added, removed, and kept — no changes written.
#
# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --backup
#     Apply the upgrade, writing a .bak first.
#
# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf
#     Apply the upgrade in-place with no backup.

#!/bin/bash
# ==============================================================================================
# ============================= conf_upgrade.sh ================================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Merges a new conf template into an existing user conf while preserving every
# value the user has already set. Run manually when the conf schema changes
# between versions — adds new keys, removes deprecated ones, and keeps the
# structure of the new template exactly.
#
#   Keys in template only  → ADDED   (placeholder/default — user fills in once)
#   Keys in target only    → REMOVED (deprecated in new version)
#   Keys in both           → KEPT    (target's value always wins, template ignored)
#   Comments / blank lines → always from template (structure follows new version)
#
# Supports all conf variable patterns: simple scalars, indexed arrays, and
# associative arrays (declare -A).
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Dry-run Mode
#   --dry-run prints the full change report (ADDED / REMOVED / KEPT) then exits
#   without writing anything. Always preview before applying to production confs.
#
# Backup Option
#   --backup writes a .bak copy of the target before overwriting. Use when
#   applying to a conf that has never been upgraded before.
#
# File Existence Guards
#   Both --template and --target are validated before any parsing begins.
#   Missing files abort immediately with a clear error.
#
# Atomic Write
#   Merged output is written to a tempfile first, then copied to the target.
#   A partial write cannot corrupt the original.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# No conf vars. All inputs are CLI flags.
#
#   --template <file>   New version conf file (source of structure and defaults)
#   --target   <file>   Existing user conf (source of real values — always preserved)
#   --dry-run           Show what would change without writing
#   --backup            Write a .bak copy of target before modifying
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --dry-run
#     Preview what would be added, removed, and kept — no changes written.
#
# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --backup
#     Apply the upgrade, writing a .bak first.
#
# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf
#     Apply the upgrade in-place with no backup.
#
# ==============================================================================================

set -uo pipefail

# ── Arguments ────────────────────────────────────────────────────────────────────────────────

TEMPLATE=""
TARGET=""
DRY_RUN=false
BACKUP=false

while [[ $# -gt 0 ]]; do
    case "$1" in
        --template) TEMPLATE="$2"; shift 2 ;;
        --target)   TARGET="$2";   shift 2 ;;
        --dry-run)  DRY_RUN=true;  shift   ;;
        --backup)   BACKUP=true;   shift   ;;
        *) echo "Unknown option: $1" >&2; exit 1 ;;
    esac
done

[[ -z "$TEMPLATE" ]] && { echo "Error: --template required" >&2; exit 1; }
[[ -z "$TARGET"   ]] && { echo "Error: --target required"   >&2; exit 1; }
[[ -f "$TEMPLATE" ]] || { echo "Error: template not found: $TEMPLATE" >&2; exit 1; }
[[ -f "$TARGET"   ]] || { echo "Error: target not found: $TARGET"     >&2; exit 1; }

# ── Parse target → KEY → full definition block ───────────────────────────────────────────────

declare -A HOST_MAP   # KEY → complete definition line(s) from user's conf

_parse_target() {
    local in_block=false cur_key="" cur_block="" line

    while IFS= read -r line || [[ -n "$line" ]]; do
        if [[ "$in_block" == true ]]; then
            cur_block+="$line"$'\n'
            # Closing ) — optional trailing whitespace and comment
            if [[ "$line" =~ ^[[:space:]]*\)[[:space:]]*(#.*)?$ ]]; then
                HOST_MAP["$cur_key"]="$cur_block"
                in_block=false; cur_key=""; cur_block=""
            fi
        else
            # declare -A KEY=(
            if [[ "$line" =~ ^[[:space:]]*declare[[:space:]]+-[a-zA-Z]+[[:space:]]+([A-Z0-9_]+)[[:space:]]*=\( ]]; then
                cur_key="${BASH_REMATCH[1]}"; in_block=true; cur_block="$line"$'\n'
            # KEY=(
            elif [[ "$line" =~ ^[[:space:]]*([A-Z0-9_]+)[[:space:]]*=\( ]]; then
                cur_key="${BASH_REMATCH[1]}"; in_block=true; cur_block="$line"$'\n'
            # KEY=value  (simple scalar)
            elif [[ "$line" =~ ^[[:space:]]*([A-Z0-9_]+)[[:space:]]*= ]]; then
                HOST_MAP["${BASH_REMATCH[1]}"]="$line"$'\n'
            fi
        fi
    done < "$TARGET"
}

# ── Walk template — collect stats (must run in current shell so arrays persist) ──────────────

declare -a ADDED=() KEPT=() REMOVED=()
declare -A TMPL_SEEN=()

_collect_stats() {
    local in_block=false cur_key="" line

    while IFS= read -r line || [[ -n "$line" ]]; do
        if [[ "$in_block" == true ]]; then
            if [[ "$line" =~ ^[[:space:]]*\)[[:space:]]*(#.*)?$ ]]; then
                in_block=false
                TMPL_SEEN["$cur_key"]=1
                if [[ -n "${HOST_MAP[$cur_key]+_}" ]]; then KEPT+=("$cur_key")
                else                                         ADDED+=("$cur_key"); fi
                cur_key=""
            fi
        else
            if [[ "$line" =~ ^[[:space:]]*declare[[:space:]]+-[a-zA-Z]+[[:space:]]+([A-Z0-9_]+)[[:space:]]*=\( ]]; then
                cur_key="${BASH_REMATCH[1]}"; in_block=true
            elif [[ "$line" =~ ^[[:space:]]*([A-Z0-9_]+)[[:space:]]*=\( ]]; then
                cur_key="${BASH_REMATCH[1]}"; in_block=true
            elif [[ "$line" =~ ^[[:space:]]*([A-Z0-9_]+)[[:space:]]*= ]]; then
                local k="${BASH_REMATCH[1]}"
                TMPL_SEEN["$k"]=1
                if [[ -n "${HOST_MAP[$k]+_}" ]]; then KEPT+=("$k")
                else                                   ADDED+=("$k"); fi
            fi
        fi
    done < "$TEMPLATE"

    for key in "${!HOST_MAP[@]}"; do
        [[ -z "${TMPL_SEEN[$key]+_}" ]] && REMOVED+=("$key")
    done
}

# ── Walk template — write merged output ──────────────────────────────────────────────────────
# Runs in a subshell (stdout redirected) — array mutations are intentionally discarded here.

_write_merged() {
    local in_block=false cur_key="" cur_block="" line

    while IFS= read -r line || [[ -n "$line" ]]; do
        if [[ "$in_block" == true ]]; then
            cur_block+="$line"$'\n'
            if [[ "$line" =~ ^[[:space:]]*\)[[:space:]]*(#.*)?$ ]]; then
                in_block=false
                if [[ -n "${HOST_MAP[$cur_key]+_}" ]]; then printf '%s' "${HOST_MAP[$cur_key]}"
                else                                        printf '%s' "$cur_block"; fi
                cur_key=""; cur_block=""
            fi
        else
            if [[ "$line" =~ ^[[:space:]]*declare[[:space:]]+-[a-zA-Z]+[[:space:]]+([A-Z0-9_]+)[[:space:]]*=\( ]]; then
                cur_key="${BASH_REMATCH[1]}"; in_block=true; cur_block="$line"$'\n'
            elif [[ "$line" =~ ^[[:space:]]*([A-Z0-9_]+)[[:space:]]*=\( ]]; then
                cur_key="${BASH_REMATCH[1]}"; in_block=true; cur_block="$line"$'\n'
            elif [[ "$line" =~ ^[[:space:]]*([A-Z0-9_]+)[[:space:]]*= ]]; then
                local k="${BASH_REMATCH[1]}"
                if [[ -n "${HOST_MAP[$k]+_}" ]]; then printf '%s' "${HOST_MAP[$k]}"
                else                                  printf '%s\n' "$line"; fi
            else
                printf '%s\n' "$line"
            fi
        fi
    done < "$TEMPLATE"
}

# ── Run ───────────────────────────────────────────────────────────────────────────────────────

_parse_target
_collect_stats

# ── Report ────────────────────────────────────────────────────────────────────────────────────

TARGET_NAME="$(basename "$TARGET")"
echo ""
echo "── conf_upgrade: $TARGET_NAME ──────────────────────────────────────────"

if [[ ${#ADDED[@]} -gt 0 ]]; then
    echo "  ADDED   (new — fill in your values where needed):"
    for k in "${ADDED[@]}"; do echo "    + $k"; done
fi

if [[ ${#REMOVED[@]} -gt 0 ]]; then
    echo "  REMOVED (deprecated — no longer in this version):"
    for k in "${REMOVED[@]}"; do echo "    - $k"; done
fi

echo "  KEPT    ${#KEPT[@]} existing vars — your values preserved"

if [[ ${#ADDED[@]} -eq 0 && ${#REMOVED[@]} -eq 0 ]]; then
    echo "  Already up to date — no changes needed."
    echo "────────────────────────────────────────────────────────────────────────"
    echo ""
    exit 0
fi

echo "────────────────────────────────────────────────────────────────────────"
echo ""

# ── Apply ─────────────────────────────────────────────────────────────────────────────────────

if [[ "$DRY_RUN" == true ]]; then
    echo "(dry-run — no changes written)"
    exit 0
fi

TMPOUT="$(mktemp)"
trap 'rm -f "$TMPOUT"' EXIT

_write_merged > "$TMPOUT"

if [[ "$BACKUP" == true ]]; then
    cp "$TARGET" "${TARGET}.bak"
    echo "Backup: ${TARGET}.bak"
fi

cp "$TMPOUT" "$TARGET"
echo "Updated: $TARGET"

-# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --dry-run

+# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --dry-run

-# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --backup

+# conf_upgrade.sh --template Configurations/master.conf.template --target Configurations/master.conf --backup


Verify the directory is clean and all references are gone:


Bash

ls /boot/config/plugins/varaverk/Deployment/ && echo "---" && grep -rn "conf_templates" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" --include="*.plg" 2>/dev/null | grep -v ".git/"

Verify conf_templates dir gone and no remaining references


conf_upgrade.sh*
deploy.sh*
---


Clean. `Deployment/conf_templates/` is gone, no remaining references. All templates now live in `Configurations/` — `master.conf.template` and `host.conf.template` alongside the live confs they serve.

Commit this?


commit this


cd /boot/config/plugins/varaverk && git status && git diff

Show working tree status and diff


<persisted-output>
Output too large (156.7KB). Full output saved to: /root/.claude/projects/-root/32e137c5-fe72-4d74-ae7b-32fcf6ef65d3/tool-results/bbxei65yb.txt

Preview (first 2KB):
On branch main
Your branch is ahead of 'origin/main' by 6 commits.
  (use "git push" to publish your local commits)

You are in a sparse checkout with 100% of tracked files present.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    Deployment/conf_templates/host.conf.template
	deleted:    Deployment/conf_templates/master.conf
	modified:   Deployment/conf_upgrade.sh
	modified:   Fallback/fallback.sh
	modified:   Media/radarr_cleanup.sh
	modified:   Media/sonarr_cleanup.sh
	modified:   Orchestrators/array_started.sh
	modified:   Plugin/unraid/System_Essentials/unraid_api_key_renew.sh
	modified:   Plugin/unraid/api/stop.php
	modified:   Plugin/unraid/js/varaverk.js
	modified:   Plugin/unraid/pages/arrs.php
	modified:   Plugin/unraid/pages/docker.php
	modified:   Plugin/unraid/pages/fallback.php
	modified:   Plugin/unraid/pages/partnership.php
	modified:   Plugin/unraid/pages/setup.php
	modified:   Plugin/varaverk.plg
	modified:   common.sh
	modified:   git_pull_execute.sh

no changes added to commit (use "git add" and/or "git commit -a")
diff --git a/Deployment/conf_templates/host.conf.template b/Deployment/conf_templates/host.conf.template
deleted file mode 100644
index a763285..0000000
--- a/Deployment/conf_templates/host.conf.template
+++ /dev/null
@@ -1,769 +0,0 @@
-#!/bin/bash
-# ==============================================================================================
-# ========================== HOST1 CONFIGURATION — unRAID-Gmer4Lfe ============================
-# ==============================================================================================
-# HOST1-specific variables — credentials, container names, share paths, failover lists.
-# Sourced after master.conf — values here extend shared profile arrays and add HOST1-specific
-# identity, credentials, and container configuration.
-#
...
</persisted-output>

On branch main
Your branch is ahead of 'origin/main' by 6 commits.
  (use "git push" to publish your local commits)

You are in a sparse checkout with 100% of tracked files present.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    Deployment/conf_templates/host.conf.template
	deleted:    Deployment/conf_templates/master.conf
	modified:   Deployment/conf_upgrade.sh
	modified:   Fallback/fallback.sh
	modified:   Media/radarr_cleanup.sh
	modified:   Media/sonarr_cleanup.sh
	modified:   Orchestrators/array_started.sh
	modified:   Plugin/unraid/System_Essentials/unraid_api_key_renew.sh
	modified:   Plugin/unraid/api/stop.php
	modified:   Plugin/unraid/js/varaverk.js
	modified:   Plugin/unraid/pages/arrs.php
	modified:   Plugin/unraid/pages/docker.php
	modified:   Plugin/unraid/pages/fallback.php
	modified:   Plugin/unraid/pages/partnership.php
	modified:   Plugin/unraid/pages/setup.php
	modified:   Plugin/varaverk.plg
	modified:   common.sh
	modified:   git_pull_execute.sh

no changes added to commit (use "git add" and/or "git commit -a")
diff --git a/Deployment/conf_templates/host.conf.template b/Deployment/conf_templates/host.conf.template
deleted file mode 100644
index a763285..0000000
--- a/Deployment/conf_templates/host.conf.template
+++ /dev/null
@@ -1,769 +0,0 @@
-#!/bin/bash
-# ==============================================================================================
-# ========================== HOST1 CONFIGURATION — unRAID-Gmer4Lfe ============================
-# ==============================================================================================
-# HOST1-specific variables — credentials, container names, share paths, failover lists.
-# Sourced after master.conf — values here extend shared profile arrays and add HOST1-specific
-# identity, credentials, and container configuration.
-#
-# Sparse checkout (git) ensures HOST2 never receives this file.
-# HOST2 never sees HOST1 credentials — clean separation at the file level.
-#
-# DO NOT put shared config here — thresholds, toggles, profiles belong in master.conf.
-# DO NOT put HOST2 variables here — they belong in host2.conf.
-#
-# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────
-#
-#   ── IDENTITY & CONNECTIVITY ────────────────────────────────────────────────────────────────
-#   IDENTITY                hostname, SSH key
-#   EMBY                    container name, URL, API key
-#   NOTIFICATIONS           Discord webhook
-#   PARTNERSHIP             auth containers, backup paths
-#
-#   ── RSYNC ──────────────────────────────────────────────────────────────────────────────────
-#   DAILY SYNC SHARES       media shares HOST1 owns and pushes to HOST2
-#   WEEKLY SYNC SHARES      appdata shares synced weekly (Sunday 2:30am)
-#   CRITICAL SYNC SHARES    appdata shares synced every 30 minutes
-#   BACKUP VERIFY           shares for checksum verification against remote
-#   HOST1 RSYNC PROFILE     host1-appdata profile for HOST1-specific appdata syncs
-#
-#   ── DOCKER ─────────────────────────────────────────────────────────────────────────────────
-#   DOCKER DAILY RESTART    containers restarted daily
-#   DOCKER WEEKLY RESTART   containers restarted weekly
-#   DOCKER WATCHDOG         memory limits, health URLs, required containers, ignore list
-#   DOCKER NETWORK CONNECT  networks and containers for docker_network_connect.sh
-#
-#   ── FALLBACK ───────────────────────────────────────────────────────────────────────────────
-#   DDNS                    DDNS containers managed by HOST1
-#   INTERNET LOSS           containers stopped when internet is lost
-#   FALLBACK TIERS          what HOST1 runs for HOST2 per tier
-#   TIER DELAYS             how long HOST1 must be down before each tier activates on HOST2
-#   RSYNC WRITEBACK         HOST1 appdata synced back on handback
-#
-#   ── MEDIA ──────────────────────────────────────────────────────────────────────────────────
-#   MEDIA PERMISSIONS       share list for media_shares_permissions.sh
-#   MEDIA CLEANER           folder lists for media_cleaner.sh
-#
-#   ── MONITORS ───────────────────────────────────────────────────────────────────────────────
-#   CERTIFICATE MONITOR     domains checked for SSL expiry
-#   SMART HEALTH            drives to skip in SMART monitoring
-#   ZFS REPORT              pools to exclude from ZFS health report
-#
-#   ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────
-#   TRANSCODES              ramdisk size, thresholds, SSD path, server array
-#
-#   ── ARR STACK ──────────────────────────────────────────────────────────────────────────────
-#   DOWNLOADERS             slskd, SABnzbd, qBittorrent credentials and URLs
-#   LIDARR                  URL, API key, path map
-#   SONARR                  URL, API key, path map
-#   RADARR                  URL, API key, path map
-#   ARR RECOVERY            per-arr recovery toggles
-#
-#   ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────
-#   SYSTEM WATCHDOG         per-host check toggles and NIC configuration
-#
-#   ── RESOURCE MANAGER ───────────────────────────────────────────────────────────────────────
-#   RESOURCE MANAGER        containers paused/stopped under memory pressure
-#
-# ==============================================================================================
-
-# ==============================================================================================
-# ── IDENTITY & CONNECTIVITY ───────────────────────────────────────────────────────────────────
-# ==============================================================================================
-
-# ━━━ Identity ━━━
-# HOST1 hostname lives in master.conf (not a credential — safe for all servers).
-# SSH key used for all server-to-server operations — rsync, failover container commands.
-# Must be in /root/.ssh/ and authorised in HOST2's /root/.ssh/authorized_keys.
-    HOST1_SSH_KEY=""            # /root/.ssh/your_key_name
-    HOST1_OWNER=""                # your Gitea/GitHub username
-    HOST1_OWNER_EMAIL=""    # your email
-
-# ━━━ Emby ━━━
-# Referenced by transcode_manager.sh, emby_session_report.sh, emby_database_repair.sh,
-# weekly_sync_maintenance.sh, and HOST1_TRANSCODE_SERVERS below.
-# API key: Emby Dashboard → API Keys → + New Key
-    HOST1_EMBY_CONTAINER="Emby"
-    HOST1_EMBY_URL="http://localhost:8096"
-    HOST1_EMBY_API_KEY=""  # Emby Dashboard → API Keys → + New Key
-
-# ━━━ Jellyfin ━━━
-# API key: Jellyfin Dashboard → Administration → API Keys → + New Key
-    HOST1_JELLYFIN_CONTAINER="Jellyfin"
-    HOST1_JELLYFIN_URL="http://localhost:8096"
-    HOST1_JELLYFIN_API_KEY=""  # Jellyfin Dashboard → API Keys → + New Key
-
-# ━━━ Gitea ━━━
-# Personal access token for gitea_ssh_setup.sh — registers this server's SSH public key
-# with Gitea so git operations use key auth instead of passwords.
-# Create in Gitea: Settings → Applications → Generate Token → scope: write:user
-    HOST1_GITEA_API_TOKEN=""    # Gitea Settings → Applications → Generate Token
-
-# ━━━ Notifications ━━━
-# Discord webhook — leave blank to disable.
-# Per-host so HOST1 and HOST2 can post to different channels or only one server notifies.
-    HOST1_DISCORD_WEBHOOK=""    # Discord channel → Integrations → Webhooks
-
-# ━━━ Partnership ━━━
-# HOST1 is always the owner (source of truth) unless --transfer has been run.
-# See README-Partnership.md and master.conf PARTNERSHIP section for full lifecycle docs.
-
-# Auth containers reconfigured on onboard/offboard.
-# Format: "ContainerName|WebUIPort"
-# On onboard → WebUI pointed at owner's Tailscale IP (mirror clicks NPM, gets owner's auth)
-# On offboard → WebUI pointed back at localhost
-    HOST1_PARTNERSHIP_AUTH_WEBUIS=(
-        "NginxProxyManager|81"
-        "Lldap-Gmer4Lfe|17170"
-        "Authelia|9091"
-        "Authelia-Secondary|9092"
-    )
-
-# XML templates (from this server's templates-user/) pushed to mirror during onboard.
-# These become the mirror's active auth stack, backed by the rsync-synced appdata.
-# Update filename if Lldap is renamed to drop the host suffix.
-    HOST1_PARTNERSHIP_AUTH_STACK=(
-        # Dependencies first — Mariadb/Redis must be healthy before Authelia starts
-        "my-Mariadb-Authelia.xml"
-        "my-Mariadb-Authelia-Secondary.xml"
-        "my-Redis-Authelia.xml"
-        "my-Redis-Authelia-Secondary.xml"
-        # Auth apps — deployed after their deps are confirmed healthy
-        "my-Authelia.xml"
-        "my-Authelia-Secondary.xml"
-        "my-NginxProxyManager.xml"
-        "my-Lldap-Gmer4Lfe.xml"
-        # Source of truth — must be available on HOST2 independently of the auth stack
-        "my-Gitea.xml"
-    )
-
-# XML templates pushed to mirror for the arr stack during onboard.
-# Deps (e.g. databases) first if any — same ordering rule as auth stack.
-    HOST1_PARTNERSHIP_ARR_STACK=(
-        # "my-Sonarr.xml"
-        # "my-Radarr.xml"
-        # "my-Lidarr.xml"
-        # "my-Prowlarr.xml"
-        # "my-Bazarr.xml"
-    )
-
-# Paths HOST2 should collect during the grace window after offboard.
-# Notified on offboard — no auto-deletion, HOST2 must collect manually within PARTNERSHIP_GRACE_HOURS.
-    HOST1_PARTNERSHIP_MIRROR_BACKUPS=(
-        # "/mnt/user/appdata-Fallback/Jayred365-Emby"
-    )
-
-# Containers parked on this server when partnership is active.
-# Stopped on onboard (owner deploys its stack instead), restarted on offboard.
-    HOST1_PARTNERSHIP_OWN_CONTAINERS=(
-        # "Emby"
-        # "NginxProxyManager"
-    )
-
-# Emby admin provisioning — toggle is owner-only, credentials are per-host.
-# Owner enables/disables the feature. Each host sets the account they want on the shared Emby.
-# On onboard: owner reads mirror's HOST*_PARTNERSHIP_EMBY_ADMIN_* and creates that account.
-# On offboard: account is deleted. Username collision → onboard exits with error.
-    HOST1_PARTNERSHIP_PROVISION_EMBY_ADMIN=false   # owner controls whether Emby is shared
-    HOST1_PARTNERSHIP_EMBY_PORT=8096
-    HOST1_PARTNERSHIP_EMBY_ADMIN_USER=""            # this server's desired Emby username
-    HOST1_PARTNERSHIP_EMBY_ADMIN_PASS=""            # this server's desired Emby password
-
-# ==============================================================================================
-# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────
-# ==============================================================================================
-
-# ━━━ Daily Sync Shares ━━━
-# Shares HOST1 pushes to all other nodes every night (1am via daily_sync_maintenance.sh).
-# Mesh model: every node pushes every media share — no ownership, no mirrors.
-# arr_sync ensures all arr libraries converge (union). rsync spreads files (additive, no --delete).
-# arr_cleanup removes true orphans based on local arr state.
-# Any node can download content to any share — it propagates to all nodes on the next cycle.
-# Nextcloud is intentionally one-directional (HOST1→HOST2 offsite backup — not arr-managed).
-# Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed.
-# For shares needing container stops or custom options — add a profile in master.conf.
-    HOST1_DAILY_SYNC_SHARES=(
-        /mnt/user/Books
-        /mnt/user/Intros
-        /mnt/user/Kids_Movies
-        /mnt/user/Kids_Tv_Shows
-        /mnt/user/Movies
-        /mnt/user/Music
-        /mnt/user/Music_Videos
-        /mnt/user/Nextcloud
-        /mnt/user/stand-up_comedy
-        /mnt/user/Sports
-        /mnt/user/Tv_Shows
-        /mnt/user/Anime_Shows-Old
-        /mnt/user/Anime_Movies-Old
-        /mnt/user/Anime_Movies
-        /mnt/user/Anime_Shows
-    )
-
-# Personal encrypted shares — synced for offsite backup, independent of media shares.
-# ZFS encrypted at dataset level — remote receives encrypted blocks, cannot read content.
-# See README-Rsync_Setup.md for ZFS encryption setup before uncommenting.
-    HOST1_PERSONAL_SHARES=(
-        # /mnt/user/HOST1-Personal     # uncomment after creating encrypted dataset
-    )
-
-# ━━━ Weekly Sync Shares ━━━
-# Appdata shares synced during the weekly maintenance window (Sunday 2:30am).
-# Containers stopped both sides before sync — full clean state guaranteed.
-# Profiles drive container stops, excludes, and options — configured in master.conf RSYNC section.
-# Order matters — Emby first (larger transfer), then Critical-Data (auth stack).
-    HOST1_WEEKLY_SYNC_SHARES=(
-        "/mnt/user/Media_Server/Emby"               # emby profile — full clean mirror
-        "/mnt/user/appdata-Fallback/Critical-Data"  # critical-data profile — auth stack
-    )
-
-# ━━━ Intermediate Sync Shares ━━━
-# Shares synced every 4 hours by intermediate_sync_maintenance.sh.
-# Uses DEFAULT_RSYNC_OPTS (no --delete) — for sub-daily propagation of metadata or watch state.
-# Full media share sync stays in the daily window. Leave empty to skip mid-day rsync entirely.
-    HOST1_INTERMEDIATE_SYNC_SHARES=(
-        # Add shares here to enable mid-day rsync
-        # Example: "/mnt/user/Emby_Metadata"
-    )
-
-# ━━━ Critical Sync Shares ━━━
-# Appdata shares synced every 30 minutes by critical_sync_maintenance.sh.
-# Format: "/path/to/share" or "/path/to/share|profile-name"
-# Order matters — Critical-Data first (auth stack), then Emby dirty sync.
-    HOST1_CRITICAL_SYNC_SHARES=(
-        "/mnt/user/appdata-Fallback/Critical-Data|critical-fallback"  # auth dirty sync — stays running
-        "/mnt/user/Media_Server/Emby|emby-fallback"                   # Emby dirty sync — stays running
-    )
-
-# ━━━ Backup Verify ━━━
-# Shares verified by backup_verify.sh — random file checksum comparison against remote.
-# Leave empty to use HOST1_DAILY_SYNC_SHARES automatically.
-# Sample size and minimum file size defined in master.conf.
-    HOST1_BACKUP_VERIFY_SHARES=(
-        # leave empty to use HOST1_DAILY_SYNC_SHARES automatically
-    )
-
-# ━━━ HOST1 Rsync Profile — host1-appdata ━━━
-# HOST1-specific appdata sync profile — extends the shared PROFILE_* arrays in master.conf.
-# Use for appdata unique to HOST1 (Organizrv2, VaultWarden, UptimeKuma etc.)
-# Shared appdata (auth stack, Emby) use dedicated profiles defined in master.conf.
-# Run manually: bash Rsync/rsync.sh /mnt/user/appdata-Fallback/HOST1-Appdata --profile=host1-appdata
-    PROFILE_RSYNC_OPTS[host1-appdata]="-av --info=progress2 --bwlimit=${PROFILE_BW_LIMIT[host1-appdata]:-8000}"
-    PROFILE_BW_LIMIT[host1-appdata]=8000
-    PROFILE_RETRY_COUNT[host1-appdata]=3
-    PROFILE_SLEEP[host1-appdata]=300
-    PROFILE_CRITICAL_CONTAINER_NAMES[host1-appdata]="Organizrv2-Gmer4Lfe UptimeKuma-Gmer4Lfe VaultWarden-Gmer4Lfe"
-    PROFILE_DELAYED_CONTAINERS[host1-appdata]=""
-    PROFILE_CONTAINER_DELAY[host1-appdata]=5
-    PROFILE_EXCLUDE_DIRS[host1-appdata]="logs *.tmp"
-
-# ==============================================================================================
-# ── DOCKER ────────────────────────────────────────────────────────────────────────────────────
-# ==============================================================================================
-
-# ━━━ Docker Daily Restart ━━━
-# Containers restarted every day via DAILY_MAINTENANCE_SCRIPTS.
-# Dispatcharr degrades over time without restart — daily is intentional, not just housekeeping.
-# Order matters — auth stack first, then media services.
-    HOST1_DAILY_RESTART_CONTAINERS=(
-        "NginxProxyManager"
-        "Lldap-Gmer4Lfe"
-        "Authelia"
-        "Authelia-Secondary"
-        "Dispatcharr-Iptv-Users"
-        "Dispatcharr"               # Live TV scheduler — degrades without daily restart
-        "Dispatcharr-Basic"
-        "ErsatzTV-Emby"
-        "Slskd"                     # Soulseek connection drops after extended uptime; restart refreshes share index
-    )
-
-# ━━━ Docker Weekly Restart ━━━
-# Less critical services restarted weekly via WEEKLY_MAINTENANCE_SCRIPTS (Sunday 2:30am).
-# Containers already stopped for weekly sync — restart adds zero extra downtime.
-    HOST1_WEEKLY_RESTART_CONTAINERS=(
-        "NextCloud"
-        "Organizrv2-Gmer4Lfe"
-        "AdGuard-Home"
-        "Immich-Gmer4Lfe"
-    )
-
-# ━━━ Docker Watchdog ━━━
-# Per-HOST1 container configuration for docker_watchdog.sh.
-# Shared thresholds and toggles live in master.conf.
-
-# Memory hard limits in MB — immediate restart if exceeded.
-# Set at "container is clearly broken" not "container is busy".
-# 20GB=20480  18GB=18432  16GB=16384  12GB=12288  8GB=8192  4GB=4096  2GB=2048  1GB=1024
-    declare -A HOST1_WATCHDOG_CONTAINERS=(
-        ["Emby"]=18432              # 18GB — large library + active transcodes
-        ["LidaTube"]=6144           # 6GB  — memory leak over time
-        ["Tdarr"]=6144              # 6GB  — encoding is memory intensive
-        ["Code-Server"]=1024        # 1GB  — should never need more
-    )
-
-# HTTP health check URLs — checked every cycle, strike system before restart.
-# Only add containers with a meaningful web interface to check.
-    declare -A HOST1_WATCHDOG_CONTAINER_URLS=(
-        ["Emby"]="http://localhost:8096"
-    )
-
-# Required containers — must always be running on HOST1.
-# Strike system before restart — repeated failures go on skip list, auto-clears on recovery.
-# Listed in dependency order — dependencies before dependents.
-    HOST1_WATCHDOG_REQUIRED_CONTAINERS=(
-        "NginxProxyManager"
-        "Lldap-Gmer4Lfe"
-        "Mariadb-Authelia"
-        "Mariadb-Authelia-Secondary"
-        "Redis-Authelia"
-        "Redis-Authelia-Secondary"
-        "Authelia"
-        "Authelia-Secondary"
-    )
-
-# Containers to skip in Tier 2 global scan — legitimately stopped or frequently restarting.
-# Watchdog leaves these alone entirely — no restart attempts, no crash loop tracking.
-    HOST1_WATCHDOG_SCAN_IGNORE=(
-        "DashGate"
-        "PIA-WG-Config-Generator"
-        "Aperture"
-        "Aperture-Kids"
-        "pgvector-18-Apeture-Kids"
-        "Pgvector18-Aperture"
-    )
-
-# Dependency ordering — skip restarting a container if its dependency is also down.
-# Prevents watchdog from restarting Authelia before Mariadb is back up.
-# SPACE-SEPARATED STRINGS — converted to array at runtime.
-    declare -A HOST1_WATCHDOG_DEPENDENCIES=(
-        ["Authelia"]="Mariadb-Authelia Redis-Authelia"
-        ["Authelia-Secondary"]="Mariadb-Authelia Redis-Authelia-Secondary"
-        ["NextCloud"]="Postgres-NextCloud"
-    )
-
-# Per-container appdata growth suppress ceilings in MB.
-# ONLY needed in specific cases — growth rate detection covers all containers automatically.
-# Use this when a container legitimately has large stable data and you want to guarantee
-# it never triggers a false-positive growth alert. Growth warnings are suppressed while the
-# container's dir stays below this ceiling; above it, warnings resume as normal.
-# 50GB=51200  25GB=25600  20GB=20480  15GB=15360  10GB=10240  5GB=5120
-    declare -A HOST1_WATCHDOG_APPDATA_SIZES=(
-        ["Tdarr"]="25600"       # 25GB — transcode cache grows legitimately during active jobs
-        ["7dtd"]="20480"        # 20GB — game server world data, expected to be large
-    )
-
-# ━━━ Network Watchdog ━━━
-# Host-specific connectivity config for Watchdogs/System/network_watchdog.sh.
-    HOST1_NETWORK_WATCHDOG_DDNS_DOMAIN="gmer4lfe.com"
-    HOST1_NETWORK_WATCHDOG_DDNS_CONTAINER="Gmer4Lfe.com"
-    HOST1_NETWORK_WATCHDOG_NPM_URL="https://gmer4lfe.com"
-
-# ━━━ Docker Network Connect ━━━
-# Containers connected to custom networks at array start by docker_network_connect.sh.
-# Networks created if they don't exist — idempotent, safe to re-run.
-    HOST1_NETWORK_CONNECT_CONTAINERS=(
-        "memcached"
-        "Npm-CrowdSec"
-    )
-
-    HOST1_NETWORK_CONNECT_NETWORKS=(
-        "high-availability"
-    )
-
-# ==============================================================================================
-# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────
-# ==============================================================================================
-
-# ━━━ DDNS ━━━
-# DDNS containers HOST1 manages — started/stopped by fallback.sh per DDNS absolute rules:
-#   Internet loss → stop immediately
-#   Failover → HOST2 starts HOST1's DDNS as Tier 1 (before any other containers)
-#   Handback → stop HOST1's DDNS on HOST2 → rsync → start containers → start local DDNS last
-    HOST1_DDNS_CONTAINERS=(
-        "Gmer4Lfe.com"
-    )
-
-# ━━━ Internet Loss ━━━
-# Containers stopped immediately on HOST1 when internet connection is lost.
-# Prevents external-facing services from operating without connectivity.
-    FALLBACK_HOST1_STOP_ON_NO_NET=(
-        "Gmer4Lfe.com"
-    )
-
-# ━━━ Fallback Tiers — HOST1 Runs for HOST2 ━━━
-# Containers HOST1 starts when HOST2 goes down.
-# Tier 1 is always immediate — vital services cannot wait.
-# Higher tiers activate after HOST2_TIER*_DELAY minutes (set in host2.conf).
-    FALLBACK_HOST1_COVERS_HOST2_TIER1=(
-        "Gmer4Lfe.us"
-        "VaultWarden-Jayred365"
-    )
-
-    FALLBACK_HOST1_COVERS_HOST2_TIER2=(
-        # "container-placeholder"
-    )
-
-    FALLBACK_HOST1_COVERS_HOST2_TIER3=(
-        # "container-placeholder"
-    )
-
-    FALLBACK_HOST1_COVERS_HOST2_TIER4=(
-        # "container-placeholder"
-    )
-
-# ━━━ Tier Delays — HOST1's Containers on HOST2 ━━━
-# How long HOST1 must be down before each tier activates on HOST2 — in minutes.
-# Tier 1 is always immediate — no delay var needed.
-    HOST1_TIER2_DELAY=240           # 4 hours  — NextCloud, Immich
-    HOST1_TIER3_DELAY=720           # 12 hours — secondary services
-    HOST1_TIER4_DELAY=1440          # 24 hours — arrs + downloaders
-
-# ━━━ Rsync Writeback — HOST1 Appdata Back on Handback ━━━
-# Syncs HOST1 appdata BACK to HOST1 when it comes back online after a failover.
-# Containers stopped before writeback — clean source, no competing writes.
-#
-# HOST1_TIER1_WRITEBACK_DELAY: short outages skip Tier 1 writeback — primary state
-# is more reliable than dirty sync data for brief outages.
-    HOST1_TIER1_WRITEBACK_DELAY=60  # skip Emby writeback if outage under 1hr
-
-# Tier 4 automatically syncs HOST1_DAILY_SYNC_SHARES — only list paths NOT in that array.
-    FALLBACK_HOST1_WRITEBACK_TIER1=(
-        "/mnt/user/Media_Server/Emby"               # watch states built up during outage
-    )
-
-    FALLBACK_HOST1_WRITEBACK_TIER2=(
-        "/mnt/user/appdata-Fallback/Important-Data" # NextCloud + Postgres — files added during outage
-    )
-
-    FALLBACK_HOST1_WRITEBACK_TIER3=(
-        # "location-placeholder"
-    )
-
-    FALLBACK_HOST1_WRITEBACK_TIER4=(
-        "/mnt/user/appdata-Fallback/Arrs_Stack"     # arr databases — downloads queued during outage
-    )
-
-# ==============================================================================================
-# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────
-# ==============================================================================================
-
-# ━━━ Media Permissions ━━━
-# Shares that media_shares_permissions.sh applies PERMISSIONS_MODE and PERMISSIONS_OWNER to.
-# Runs first in DAILY_MAINTENANCE_SCRIPTS — arr cleanup depends on correct ownership.
-    HOST1_MEDIA_PERMISSION_SHARES=(
-        /mnt/user/Anime_Movies
-        /mnt/user/Anime_Movies-Old
-        /mnt/user/Anime_Shows
-        /mnt/user/Anime_Shows-Old
-        /mnt/user/appcache
-        /mnt/user/Books
-        /mnt/user/Downloads
-        /mnt/user/Games
-        /mnt/user/Intros
-        /mnt/user/Kids_Movies
-        /mnt/user/Kids_Tv_Shows
-        /mnt/user/Movie_Recordings
-        /mnt/user/Movies
-        /mnt/user/Music
-        /mnt/user/Music_Videos
-        /mnt/user/Photo
-        /mnt/user/Sports
-        /mnt/user/stand-up_comedy
-        /mnt/user/Temp_Storage
-        /mnt/user/Tv_Recordings
-        /mnt/user/Tv_Shows
-        /mnt/user/YouTube
-    )
-
-# ━━━ Media Cleaner ━━━
-# Folder lists for media_cleaner.sh — two profiles: anime and media.
-# File patterns shared across all servers — defined in master.conf.
-# Called via DAILY_MAINTENANCE_SCRIPTS. Run manually: Media/media_cleaner.sh anime|media
-    HOST1_ANIME_CLEAN_FOLDERS=(
-        /mnt/user/Anime_Movies
-        /mnt/user/Anime_Movies-Old
-        /mnt/user/Anime_Shows
-        /mnt/user/Anime_Shows-Old
-    )
-
-    HOST1_MEDIA_CLEAN_FOLDERS=(
-        /mnt/user/Kids_Movies
-        /mnt/user/Kids_Tv_Shows
-        /mnt/user/Movies
-        /mnt/user/Music
-        /mnt/user/Sports
-        /mnt/user/stand-up_comedy
-        /mnt/user/Tv_Shows
-    )
-
-# ==============================================================================================
-# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────
-# ==============================================================================================
-
-# ━━━ Certificate Monitor ━━━
-# Domains checked via direct openssl connection — not relying on NPM's certificate state.
-# Checks the actual certificate served, not what NPM thinks it has.
-# Thresholds (CERT_WARN_DAYS, CERT_CRIT_DAYS) defined in master.conf.
-    HOST1_CERT_MONITOR_DOMAINS=(
-        "Gmer4Lfe.com"
-        "Gmer4Lfe.us"
-    )
-
-# ━━━ SMART Health ━━━
-# Drives skipped in SMART attribute monitoring — hardware is server-specific.
-# Thresholds read from dynamix.cfg at runtime — fallbacks in master.conf.
-    HOST1_SMART_IGNORE_DRIVES=(
-        "sda"                       # boot USB — SMART not meaningful on flash drives
-    )
-
-# ━━━ ZFS Report ━━━
-# Pools excluded from the weekly ZFS health report — reduces noise from single-disk array pools.
-# These are individual array disks formatted as ZFS — converting to XFS over time via unBalance.
-# Pool health thresholds defined in master.conf.
-    HOST1_ZFS_REPORT_IGNORE_POOLS=(
-        "disk5"
-        "disk6"
-        "disk8"
-        "disk9"
-        "disk10"
-    )
-
-# ==============================================================================================
-# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────
-# ====================================================================================


There are other pre-existing uncommitted changes I shouldn't bundle in. I'll stage only the files I touched.


git add Deployment/conf_templates/host.conf.template Deployment/conf_templates/master.conf Deployment/conf_upgrade.sh Plugin/varaverk.plg git_pull_execute.sh && git log --oneline -5

Stage only the files changed in this task and show recent log


129e8ae Fix Emby HTTP 500 on PlayedItems — convert DatePlayed to yyyyMMddHHmmss
ca0d17e All rsync flags re-enabled — HOST2 rebuild complete
30e7c2f Detect containers running behind already-pulled images
819f5e6 Remove old images by ID after container updates, not just dangling prune
1e54752 Fix digest falsely counting = style state entries as system watchdog strikes


git commit -m "$(cat <<'EOF'
Consolidate conf templates into Configurations/ — remove stale Deployment/conf_templates/

Deployment/conf_templates/ had two stale files: host.conf.template was a copy of host1.conf
(not a HOSTN template), and master.conf had hardcoded hostnames and an old appdata DATA_DIR path.
The correct templates already exist in Configurations/ (master.conf.template, host.conf.template).

Updated varaverk.plg and git_pull_execute.sh to read from Configurations/ directly.
Removed TMPL_DIR variable from git_pull_execute.sh — CONF_DIR covers both.
EOF
)"

Commit the template consolidation


[main 3937d5e] Consolidate conf templates into Configurations/ — remove stale Deployment/conf_templates/
 5 files changed, 10 insertions(+), 2213 deletions(-)
 delete mode 100644 Deployment/conf_templates/host.conf.template
 delete mode 100644 Deployment/conf_templates/master.conf


Done. 5 files changed — 2 deleted, 3 updated. `Deployment/conf_templates/` is gone; all templates live in `Configurations/` and all scripts point there.


push it


Push to origin


remote: . Processing 1 references
remote: Processed 1 references in total
To ssh://192.168.50.2:221/FailedProxy/Varaverk.git
   af31335..3937d5e  main -> main


Pushed. Gitea is up to date.


We're reviewing and cleaning up Varaverk's onboarding setup before testing on HOST2. All conf templates are now consolidated into Configurations/ and pushed. Next: install the plugin on HOST2 and walk through first launch. (disable recaps in /config)


what other commits are there


git status --short && git log origin/main..HEAD --oneline

Show uncommitted changes and any unpushed commits


 M Fallback/fallback.sh
 M Media/radarr_cleanup.sh
 M Media/sonarr_cleanup.sh
 M Orchestrators/array_started.sh
 M Plugin/unraid/System_Essentials/unraid_api_key_renew.sh
 M Plugin/unraid/api/stop.php
 M Plugin/unraid/js/varaverk.js
 M Plugin/unraid/pages/arrs.php
 M Plugin/unraid/pages/docker.php
 M Plugin/unraid/pages/fallback.php
 M Plugin/unraid/pages/partnership.php
 M Plugin/unraid/pages/setup.php
 M common.sh


No unpushed commits — everything pushed. There are 13 modified but uncommitted files though:

- `Fallback/fallback.sh`
- `Media/radarr_cleanup.sh`, `sonarr_cleanup.sh`
- `Orchestrators/array_started.sh`
- `Plugin/unraid/System_Essentials/unraid_api_key_renew.sh`
- `Plugin/unraid/api/stop.php`
- `Plugin/unraid/js/varaverk.js`
- `Plugin/unraid/pages/arrs.php`, `docker.php`, `fallback.php`, `partnership.php`, `setup.php`
- `common.sh`

Want me to diff them so you can see what's in each?


push them


Show diff summary of all uncommitted changes


 Fallback/fallback.sh                               |  4 ++-
 Media/radarr_cleanup.sh                            | 37 +++++++++----------
 Media/sonarr_cleanup.sh                            | 37 +++++++++----------
 Orchestrators/array_started.sh                     |  7 ++++
 .../System_Essentials/unraid_api_key_renew.sh      | 19 ++++++++--
 Plugin/unraid/api/stop.php                         | 13 +++----
 Plugin/unraid/js/varaverk.js                       | 41 ++++++++++++++++++++++
 Plugin/unraid/pages/arrs.php                       | 33 +++++------------
 Plugin/unraid/pages/docker.php                     |  6 +++-
 Plugin/unraid/pages/fallback.php                   |  5 ++-
 Plugin/unraid/pages/partnership.php                | 20 +----------
 Plugin/unraid/pages/setup.php                      |  4 ++-
 common.sh                                          |  5 +--
 13 files changed, 137 insertions(+), 94 deletions(-)


Show full diff of all uncommitted changes