diff --git a/Plugin/unraid/Tools/ui_map_build.php b/Plugin/unraid/Tools/ui_map_build.php new file mode 100644 index 0000000..16498d3 --- /dev/null +++ b/Plugin/unraid/Tools/ui_map_build.php @@ -0,0 +1,217 @@ + ['file','subsection','fields'] +foreach (vv_get_conf_files() as $file) { + foreach (vv_conf_all_groups($file) as $g) { + if (empty($g['fields'])) continue; + $sections[$file . "\0" . $g['subsection']] = $g; + } +} + +// ── Route 1: the Scheduler, one script at a time ───────────────────────────────────────────── +// A section may be reached through several scripts — a shared threshold belongs to whichever +// scripts read it — so routes accumulate rather than overwrite. +$routes = []; // section key => list of human routes +foreach (VV_SCRIPT_CONF_SECTIONS as $script => $subs) { + $label = vv_pretty_label(basename($script, '.sh')); + foreach ((array) $subs as $sub) { + foreach ($sections as $k => $g) { + if (strcasecmp($g['subsection'], $sub) !== 0) continue; + $routes[$k][] = "Scheduler tab → **{$label}** → Config → *{$g['subsection']}*"; + } + } +} + +// ── Route 2: pages that show sections by subject ───────────────────────────────────────────── +foreach (VV_UI_SECTION_SURFACES as $surface) { + $re = '/\b' . preg_quote((string) $surface['match'], '/') . '\b/i'; + foreach ($sections as $k => $g) { + if (!preg_match($re, (string) $g['subsection'])) continue; + $routes[$k][] = $surface['route'] . " → *{$g['subsection']}*"; + } +} + +// ── Route 3: pages with a purpose-built control for one named setting ──────────────────────── +// Read out of the page source rather than declared, so a control that is added or removed moves +// the map with it. The pattern is the literal key in a change payload or a toggle call — the one +// shape these pages have in common. Keys assembled at runtime are invisible here and correctly +// fall through to the conf-only list rather than being guessed at. +// Every key this host's confs actually define, so a declared route can be checked against +// reality rather than trusted. +$known = []; +foreach ($sections as $g) foreach ($g['fields'] as $f) $known[$f['key']] = true; + +// This machine's host slot, for substituting HOSTN in declared keys. +$slot = 'HOST1'; +foreach (vv_get_conf_files() as $f) { + if (preg_match('/^host(\d+)\.conf$/i', $f, $hm)) { $slot = 'HOST' . $hm[1]; break; } +} + +$keyRoutes = []; // KEY => list of routes +$stale = []; +foreach (VV_UI_PAGE_ROUTES as $page => $spec) { + $route = is_array($spec) ? $spec['route'] : $spec; + $src = @file_get_contents(dirname(__DIR__) . '/pages/' . $page); + if ($src === false) { fwrite(STDERR, "note: $page not found, skipped\n"); continue; } + if (preg_match_all('/(?:key|name)\s*:\s*\'([A-Z][A-Z0-9_]{3,})\'|\(this,\s*\'([A-Z][A-Z0-9_]{3,})\'\)/', + $src, $m, PREG_SET_ORDER)) { + foreach ($m as $hit) { + $key = $hit[1] !== '' ? $hit[1] : ($hit[2] ?? ''); + if ($key !== '' && isset($known[$key])) $keyRoutes[$key][] = $route; + } + } + foreach ((array) (is_array($spec) ? ($spec['also'] ?? []) : []) as $decl) { + $key = str_replace('HOSTN', $slot, $decl); + if (isset($known[$key])) { $keyRoutes[$key][] = $route; continue; } + $stale[] = "$page declares $decl (→ $key) which no conf defines"; + } +} + +// ── How each control is described to someone who has to find it ────────────────────────────── +const UI_CONTROL_WORDS = [ + 'bool' => 'a switch', + 'int' => 'a number box', + 'enum' => 'a dropdown', + 'secret' => 'a masked box with a **Show** button', + 'lines' => 'a list, one entry per line', + 'path' => 'a text box', + 'text' => 'a text box', +]; + +$md = "# Where every setting lives in the web UI\n\n"; +$md .= "Generated by `Tools/ui_map_build.php` — do not edit by hand.\n\n"; +$md .= "Every setting below can be changed in the browser. Nothing here needs a conf file opened\n" + . "over SSH, and the routes are what to tell someone who asks where a setting is.\n\n"; +$md .= "Two surfaces show settings, and which one holds a given section depends on what the\n" + . "section is about:\n\n"; +$md .= "- **The Scheduler tab** shows the settings belonging to one script. Pick the script, open\n" + . " **Config**, and its sections appear there.\n"; +$md .= "- **The AI tab** shows the AI sections together under **Settings → Configuration**.\n\n"; +$md .= "Both write through the same guarded path: the change is validated, the conf is backed up,\n" + . "the result is syntax-checked and read back, and a bad write is rolled back.\n\n"; +$md .= "A setting is edited by finding its row and changing the control described below. The Save\n" + . "button sends only what was actually changed.\n\n---\n"; + +// A section with no section-level route may still have per-key routes, if a page carries a +// purpose-built control for some of its settings. That is a reachable section — just one whose +// route is stated per row rather than once at the top. +foreach ($sections as $k => $g) { + if (!empty($routes[$k])) continue; + foreach ($g['fields'] as $f) { + if (!empty($keyRoutes[$f['key']])) { $routes[$k][] = '__perkey__'; break; } + } +} + +$reachable = 0; $unreachable = []; +ksort($sections); +foreach ($sections as $k => $g) { + if (empty($routes[$k])) { $unreachable[] = $g; continue; } + $reachable++; + + $md .= "\n## " . $g['subsection'] . "\n\n"; + $md .= "In `" . $g['file'] . "`.\n\n"; + + $seen = array_values(array_diff(array_unique($routes[$k]), ['__perkey__'])); + if (!$seen) { + $md .= "No single page shows this section. Individual settings below carry their own route.\n\n"; + } else { + $md .= count($seen) === 1 ? "Route: " . $seen[0] . "\n\n" + : "Reachable from:\n\n" . implode("\n", array_map(fn($r) => "- $r", $seen)) . "\n\n"; + } + + $md .= "| Setting | Control | Where | What it does |\n|---|---|---|---|\n"; + foreach ($g['fields'] as $f) { + $ctl = UI_CONTROL_WORDS[$f['widget'] ?? 'text'] ?? 'a text box'; + if (($f['widget'] ?? '') === 'enum' && !empty($f['choices'])) { + $ctl .= ' (' . implode(', ', array_map(fn($c) => $c['value'], $f['choices'])) . ')'; + } + if (!empty($f['unit'])) $ctl .= ', in ' . $f['unit']; + if (isset($f['min'])) $ctl .= ', ' . $f['min'] . '–' . $f['max']; + // The conf's own comment. Newlines and pipes would break the table row. + $desc = trim(preg_replace('/\s+/', ' ', (string) ($f['desc'] ?? ''))); + $desc = str_replace('|', '\\|', $desc); + if (mb_strlen($desc) > 400) $desc = mb_substr($desc, 0, 397) . '…'; + // A per-key route wins for that row: a purpose-built control is a better answer than + // "somewhere in this section", and it is often on a different page entirely. + $where = !empty($keyRoutes[$f['key']]) + ? implode(', ', array_unique($keyRoutes[$f['key']])) + : ($seen ? 'in this section' : '—'); + $md .= '| `' . $f['key'] . '` | ' . $ctl . ' | ' . $where . ' | ' + . ($desc !== '' ? $desc : '—') . " |\n"; + } +} + +if ($unreachable) { + $md .= "\n---\n\n## Settings with no route through the UI\n\n"; + $md .= "These sections are not rendered by any page, so they can only be changed by editing\n" + . "the conf file. If one of these is asked about, say so plainly rather than inventing a\n" + . "route — mapping it into the Scheduler is a code change, not a setting.\n\n"; + foreach ($unreachable as $g) { + $keys = implode(', ', array_map(fn($f) => '`' . $f['key'] . '`', $g['fields'])); + $md .= '- **' . $g['subsection'] . '** (`' . $g['file'] . "`) — $keys\n"; + } +} + +$existing = is_readable($outAbs) ? file_get_contents($outAbs) : null; +$same = $existing !== null && $existing === $md; + +foreach ($stale as $s) fwrite(STDERR, "STALE ROUTE: $s\n"); +printf("%d sections reachable, %d with no UI route\n", $reachable, count($unreachable)); +printf("%d settings documented\n", array_sum(array_map( + fn($k) => empty($routes[$k]) ? 0 : count($sections[$k]['fields']), array_keys($sections)))); + +if ($check) { + echo $same ? "up to date\n" : "OUT OF DATE — re-run without --check\n"; + exit($same ? 0 : 1); +} +if ($same) { echo "no change\n"; exit(0); } + +if (@file_put_contents($outAbs, $md) === false) { + fwrite(STDERR, "could not write $outAbs\n"); + exit(1); +} +printf("wrote %s (%d bytes)\n", $outRel, strlen($md)); diff --git a/Plugin/unraid/include/confform.php b/Plugin/unraid/include/confform.php index 0917498..bdb1990 100644 --- a/Plugin/unraid/include/confform.php +++ b/Plugin/unraid/include/confform.php @@ -94,6 +94,50 @@ require_once __DIR__ . '/config.php'; // confform.php — script→conf-section mapping, field parsing, and write-back. +// Which page shows conf sections by subject rather than by script, and the route a person would +// be told to follow to reach it. Declared once because two things need it and they must agree: +// the page itself builds its ?sections= query from this, and Tools/ui_map_build.php turns it into +// navigation instructions the assistant can give. A route written down in only one of those two +// places is a route that goes stale the first time a card moves. +// +// `match` is the whole-word needle matched against section headers — see api/confform.php. +const VV_UI_SECTION_SURFACES = [ + [ + 'match' => 'ai', + 'tab' => 'AI', + 'route' => 'AI tab → Settings → Configuration', + ], + [ + 'match' => 'partnership', + 'tab' => 'Partnership', + // Array fields only — the card is one collapsible block per list, because these are + // container lists dozens of lines long and a flat form would be unreadable. + 'route' => 'Partnership tab → Array Settings', + ], +]; + +// Pages that edit named keys rather than whole sections — a purpose-built control for one +// setting, not a form over a conf section. Only the route is declared: the keys themselves are +// read out of the page source by Tools/ui_map_build.php, so a control added or removed changes +// the map without anyone remembering to update a list. +// +// A page missing from here is not broken; its settings simply appear as conf-only in the map, +// which is the honest answer until someone gives it a route. +// `also` lists keys the page assembles at runtime, which no amount of reading the source will +// reveal — settings.php writes HOST1_DISCORD_WEBHOOK through a PHP variable holding the host +// slot. Written with HOSTN, substituted per machine, the same convention conf_upgrade uses. The +// generator checks each one exists and complains if it does not, so a stale entry is loud rather +// than a route to a control that was removed. +const VV_UI_PAGE_ROUTES = [ + 'settings.php' => ['route' => 'Settings tab', + 'also' => ['HOSTN_DISCORD_WEBHOOK', 'HOSTN_STORAGE_MODE_INTERNAL']], + 'fallback.php' => ['route' => 'Fallback tab'], + 'rsync.php' => ['route' => 'Rsync tab'], + 'arrs.php' => ['route' => 'Arrs tab'], + 'watchdog.php' => ['route' => 'Watchdog tab'], + 'docker.php' => ['route' => 'Docker tab'], +]; + // Map: script relative id → subsection names (must match # ━━━ Name ━━━ or # ── Name ── headers). const VV_SCRIPT_CONF_SECTIONS = [ // Orchestrators diff --git a/Plugin/unraid/pages/ai.php b/Plugin/unraid/pages/ai.php index 5e677c7..abc4bd9 100644 --- a/Plugin/unraid/pages/ai.php +++ b/Plugin/unraid/pages/ai.php @@ -461,6 +461,10 @@ vv_ai_chat_markup('vv-ai', [ // the Scheduler uses, so there is one allowlist and one write path rather than an AI-shaped // copy of both — api/ai.php deliberately has no conf-writing action at all. const API_CONF = '/plugins/varaverk/api/confform.php'; + // Which sections this tab claims, from the one registry that also generates the UI map. Hard + // coding "ai" here again would be a second place for the answer to live, and the map would go + // on describing a route this page had stopped taking. + const CONF_SECTIONS = ; // The conversation itself — profiles, transcript, composer, source viewer, storage — is // include/ai_chat.php. What remains on this page is everything that surrounds it and exists @@ -1098,7 +1102,7 @@ vv_ai_chat_markup('vv-ai', [ function loadConf() { if (confLoaded) return; confLoaded = true; - fetch(API_CONF + '?sections=ai').then(r => r.json()) + fetch(API_CONF + '?sections=' + encodeURIComponent(CONF_SECTIONS)).then(r => r.json()) .then(d => { if (!d.ok) { $('vv-ai-conf').innerHTML = `
${esc(d.error || 'could not be read')}
`; return; } VvConfUI.render('vv-ai-conf', d.groups || []); diff --git a/Plugin/unraid/pages/readme/ui-map.md b/Plugin/unraid/pages/readme/ui-map.md new file mode 100644 index 0000000..c87ac16 --- /dev/null +++ b/Plugin/unraid/pages/readme/ui-map.md @@ -0,0 +1,1445 @@ +# Where every setting lives in the web UI + +Generated by `Tools/ui_map_build.php` — do not edit by hand. + +Every setting below can be changed in the browser. Nothing here needs a conf file opened +over SSH, and the routes are what to tell someone who asks where a setting is. + +Two surfaces show settings, and which one holds a given section depends on what the +section is about: + +- **The Scheduler tab** shows the settings belonging to one script. Pick the script, open + **Config**, and its sections appear there. +- **The AI tab** shows the AI sections together under **Settings → Configuration**. + +Both write through the same guarded path: the change is validated, the conf is backed up, +the result is syntax-checked and read back, and a bad write is rolled back. + +A setting is edited by finding its row and changing the control described below. The Save +button sends only what was actually changed. + +--- + +## Arr Recovery Toggles + +In `host1.conf`. + +Route: Scheduler tab → **Arrs Failed Stalled Recovery** → Config → *Arr Recovery Toggles* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_LIDARR_RECOVERY` | a switch | in this section | HOST1 only — exits cleanly on HOST2 | +| `HOST1_SONARR_RECOVERY` | a switch | in this section | — | +| `HOST1_RADARR_RECOVERY` | a switch | in this section | — | + +## Backup Verify + +In `host1.conf`. + +Route: Scheduler tab → **Backup Verify** → Config → *Backup Verify* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_BACKUP_VERIFY_SHARES` | a list, one entry per line | in this section | Leave empty to use HOST1_DAILY_SYNC_SHARES automatically. | + +## Certificate Monitor + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Cert Monitor** → Config → *Certificate Monitor* +- Scheduler tab → **Weekly Health Digest** → Config → *Certificate Monitor* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_CERT_MONITOR_DOMAINS` | a list, one entry per line | in this section | — | + +## Corruption Scan + +In `host1.conf`. + +Route: Scheduler tab → **Arr Corruption Scan** → Config → *Corruption Scan* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_FFPROBE_CONTAINER` | a text box | in this section | — | +| `HOST1_FFPROBE_BIN` | a text box | in this section | — | +| `HOST1_FFPROBE_PATH_MAP` | a list, one entry per line | in this section | — | + +## Critical Sync Shares + +In `host1.conf`. + +Route: Scheduler tab → **Critical Sync Maintenance** → Config → *Critical Sync Shares* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_CRITICAL_SYNC_SHARES` | a list, one entry per line | in this section | Appdata shares synced every 30 minutes. Format: "/path/to/share" or "/path/to/share\|profile-name" | + +## DDNS + +In `host1.conf`. + +Route: Scheduler tab → **Fallback** → Config → *DDNS* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_DDNS_CONTAINERS` | a list, one entry per line | in this section | DDNS containers this host manages. | + +## Daily Sync Shares + +In `host1.conf`. + +Route: Scheduler tab → **Daily Sync Maintenance** → Config → *Daily Sync Shares* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_DAILY_SYNC_SHARES` | a list, one entry per line | in this section | Media shares this host pushes to all other nodes every night. Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed. | + +## Docker Daily Restart + +In `host1.conf`. + +Route: Scheduler tab → **Docker Daily Restart** → Config → *Docker Daily Restart* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_DAILY_RESTART_CONTAINERS` | a list, one entry per line | in this section | — | + +## Docker Network Connect + +In `host1.conf`. + +Route: Scheduler tab → **Docker Network Connect** → Config → *Docker Network Connect* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_NETWORK_CONNECT_CONTAINERS` | a list, one entry per line | in this section | — | +| `HOST1_NETWORK_CONNECT_NETWORKS` | a list, one entry per line | in this section | — | + +## Docker Watchdog + +In `host1.conf`. + +Route: Scheduler tab → **Docker Watchdog** → Config → *Docker Watchdog* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_WATCHDOG_CONTAINERS` | a list, one entry per line | in this section | Memory hard limits in MB — immediate restart if exceeded. 20GB=20480 16GB=16384 8GB=8192 4GB=4096 2GB=2048 1GB=1024 | +| `HOST1_WATCHDOG_CONTAINER_URLS` | a list, one entry per line | in this section | HTTP health check URLs — checked every cycle. | +| `HOST1_WATCHDOG_CONTAINER_API_CHECKS` | a list, one entry per line | in this section | API-level health checks — catches HTTP-200-but-internally-frozen containers (DB lock, deadlocked thread, etc.) that a basic HTTP check above would miss. Pick an endpoint that forces a real DB round-trip — a lightweight status endpoint may stay 200 even while the rest of the app is locked up. Format: ["ContainerName"]="url\|APIKey" | +| `HOST1_WATCHDOG_REQUIRED_CONTAINERS` | a list, one entry per line | in this section | Required containers — must always be running. | +| `HOST1_WATCHDOG_SCAN_IGNORE` | a list, one entry per line | in this section | Containers to skip in Tier 2 global scan. | +| `HOST1_WATCHDOG_DEPENDENCIES` | a list, one entry per line | in this section | Dependency ordering — skip restarting a container if its dependency is also down. | +| `HOST1_WATCHDOG_APPDATA_SIZES` | a list, one entry per line | in this section | Per-container appdata growth suppress ceilings in MB. | + +## Docker Weekly Restart + +In `host1.conf`. + +Route: Scheduler tab → **Docker Weekly Restart** → Config → *Docker Weekly Restart* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_WEEKLY_RESTART_CONTAINERS` | a list, one entry per line | in this section | — | + +## Downloaders + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Downloaders Reset** → Config → *Downloaders* +- Scheduler tab → **Resource Watchdog** → Config → *Downloaders* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_SLSKD_URL` | a text box | in this section | — | +| `HOST1_SLSKD_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_SLSKD_FAILED_IMPORTS_DIR` | a text box | in this section | — | +| `HOST1_SABNZBD_URL` | a text box | in this section | — | +| `HOST1_SABNZBD_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_QBIT_URL` | a text box | in this section | — | +| `HOST1_QBIT_USERNAME` | a text box | in this section | — | +| `HOST1_QBIT_PASSWORD` | a masked box with a **Show** button | in this section | — | + +## Emby + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Emby Session Report** → Config → *Emby* +- Scheduler tab → **Server Reboot** → Config → *Emby* +- Scheduler tab → **Playback Aware Lidarr Discovery** → Config → *Emby* +- Scheduler tab → **Playback Aware Radarr Discovery** → Config → *Emby* +- Scheduler tab → **Playback Aware Sonarr Discovery** → Config → *Emby* +- Scheduler tab → **Emby To Lidarr Sync** → Config → *Emby* +- Scheduler tab → **Emby To Radarr Sync** → Config → *Emby* +- Scheduler tab → **Emby To Sonarr Sync** → Config → *Emby* +- Scheduler tab → **Emby Database Repair** → Config → *Emby* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_EMBY_CONTAINER` | a text box | in this section | — | +| `HOST1_EMBY_URL` | a text box | in this section | — | +| `HOST1_EMBY_API_KEY` | a masked box with a **Show** button | in this section | — | + +## Intermediate Sync Shares + +In `host1.conf`. + +Route: Scheduler tab → **Intermediate Sync Maintenance** → Config → *Intermediate Sync Shares* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_INTERMEDIATE_SYNC_SHARES` | a list, one entry per line | in this section | Shares synced every 4 hours. Leave empty to skip mid-day rsync. | + +## Internet Loss + +In `host1.conf`. + +Route: Scheduler tab → **Fallback** → Config → *Internet Loss* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `FALLBACK_HOST1_STOP_ON_NO_NET` | a list, one entry per line | in this section | Containers stopped immediately when internet is lost. | + +## Lidarr + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Lidarr Cleanup** → Config → *Lidarr* +- Scheduler tab → **Lidarr Missing Art** → Config → *Lidarr* +- Scheduler tab → **Lidarr Release Fixer** → Config → *Lidarr* +- Scheduler tab → **Lidarr Duplicate Artist Cleanup** → Config → *Lidarr* +- Scheduler tab → **Playback Aware Lidarr Discovery** → Config → *Lidarr* +- Scheduler tab → **Emby To Lidarr Sync** → Config → *Lidarr* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_LIDARR_URL` | a text box | in this section | — | +| `HOST1_LIDARR_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_LIDARR_MUSIC_ROOT` | a text box | in this section | — | +| `HOST1_FANART_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_LASTFM_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_LIDARR_PATH_MAP` | a list, one entry per line | in this section | — | + +## Media Cleaner + +In `host1.conf`. + +Route: Scheduler tab → **Media Cleaner** → Config → *Media Cleaner* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_ANIME_CLEAN_FOLDERS` | a list, one entry per line | in this section | — | +| `HOST1_MEDIA_CLEAN_FOLDERS` | a list, one entry per line | in this section | — | + +## Media Permissions + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Media Shares Permissions** → Config → *Media Permissions* +- Scheduler tab → **Bulk Permissions Repair** → Config → *Media Permissions* +- Scheduler tab → **Trailer Folder Migration** → Config → *Media Permissions* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_MEDIA_PERMISSION_SHARES` | a list, one entry per line | in this section | — | + +## Network Watchdog + +In `host1.conf`. + +Route: Scheduler tab → **Network Watchdog** → Config → *Network Watchdog* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_NETWORK_WATCHDOG_DDNS_DOMAIN` | a text box | in this section | — | +| `HOST1_NETWORK_WATCHDOG_DDNS_CONTAINER` | a text box | in this section | — | +| `HOST1_NETWORK_WATCHDOG_NPM_URL` | a text box | in this section | — | + +## Notifications + +In `host1.conf`. + +No single page shows this section. Individual settings below carry their own route. + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_DISCORD_WEBHOOK` | a text box | Settings tab | Discord webhook — leave blank to disable. | + +## Ollama + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Ai Index** → Config → *Ollama* +- Scheduler tab → **Ai Query** → Config → *Ollama* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_OLLAMA_URL` | a text box | in this section | e.g. http://localhost:11434 — empty if no local Ollama | +| `HOST1_OLLAMA_GPU_UUID` | a text box | in this section | pins Ollama to one card on multi-GPU hosts | +| `HOST1_OLLAMA_MODEL` | a text box | in this section | generation — must fully offload; see README-AI.md | + +## PARTNERSHIP + +In `host1.conf`. + +Route: Partnership tab → Array Settings → *PARTNERSHIP* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_PARTNERSHIP_AUTH_WEBUIS` | a list, one entry per line | in this section | Auth containers reconfigured on onboard/offboard. Format: "ContainerName\|WebUIPort" | +| `HOST1_PARTNERSHIP_AUTH_STACK` | a list, one entry per line | in this section | XML templates pushed to mirror during onboard — auth stack. Dependencies (databases) must come before apps that depend on them. | +| `HOST1_PARTNERSHIP_ARR_STACK` | a list, one entry per line | in this section | XML templates pushed to mirror during onboard — arr stack. | +| `HOST1_PARTNERSHIP_SERVICES_STACK` | a list, one entry per line | in this section | XML templates pushed to mirror during onboard — other services (not auth, not arr). | +| `HOST1_PARTNERSHIP_MIRROR_BACKUPS` | a list, one entry per line | in this section | Paths the partner should collect during the grace window after offboard. | +| `HOST1_PARTNERSHIP_OWN_CONTAINERS` | a list, one entry per line | in this section | Containers parked on this server when partnership is active. | +| `HOST1_PARTNERSHIP_REPLACE_CONTAINERS` | a list, one entry per line | in this section | Containers stopped on THIS server before deploying the mirror's stack on onboard. | +| `HOST1_PARTNERSHIP_ARR_REPLACE_CONTAINERS` | a list, one entry per line | in this section | Arr containers stopped on this server when mirror's arr stack is deployed. | +| `HOST1_PARTNERSHIP_PROVISION_EMBY_ADMIN` | a switch | in this section | owner controls whether Emby is shared | +| `HOST1_PARTNERSHIP_EMBY_PORT` | a number box | in this section | — | +| `HOST1_PARTNERSHIP_EMBY_ADMIN_USER` | a text box | in this section | this server's desired Emby username | +| `HOST1_PARTNERSHIP_EMBY_ADMIN_PASS` | a masked box with a **Show** button | in this section | this server's desired Emby password | + +## Radarr + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Radarr Cleanup** → Config → *Radarr* +- Scheduler tab → **Radarr Classification Scan** → Config → *Radarr* +- Scheduler tab → **Playback Aware Radarr Discovery** → Config → *Radarr* +- Scheduler tab → **Radarr Tmdb Removed** → Config → *Radarr* +- Scheduler tab → **Emby To Radarr Sync** → Config → *Radarr* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_RADARR_URL` | a text box | in this section | — | +| `HOST1_RADARR_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_TMDB_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_RADARR_MOVIES_ROOT` | a text box | in this section | — | +| `HOST1_RADARR_GENERAL_ROOT` | a text box | in this section | rootFolderPath literal for the general root — target for reverse-kids-leak moves | +| `HOST1_RADARR_KIDS_ROOT` | a text box | in this section | rootFolderPath literal, as reported by Radarr API | +| `HOST1_RADARR_ANIME_ROOT` | a text box | in this section | — | +| `HOST1_RADARR_DOWNLOAD_DIR` | a text box | in this section | — | +| `HOST1_RADARR_DOWNLOAD_CONTAINER_DIR` | a text box | in this section | — | +| `HOST1_RADARR_PATH_MAP` | a list, one entry per line | in this section | — | + +## SMART Health + +In `host1.conf`. + +Route: Scheduler tab → **Smart Health** → Config → *SMART Health* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_SMART_IGNORE_DRIVES` | a list, one entry per line | in this section | — | + +## SYSTEM WATCHDOG + +In `host1.conf`. + +Route: Scheduler tab → **Watchdog Orchestrator** → Config → *SYSTEM WATCHDOG* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_SYS_WATCHDOG_NIC` | a text box | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_DOCKER_DAEMON` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_ROOTFS` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_KERNEL_OOPS` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_FD` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_BOOT` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_OOM` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_RAM` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_LOG` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_ARC` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_CPU_TEMP` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_LOAD` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_ZOMBIES` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_CONTAINERS` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_TMP` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_MDSTAT` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_NETWORK` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_SSHD` | a switch | in this section | — | +| `HOST1_SYS_WATCHDOG_CHECK_RUNAWAY` | a switch | in this section | — | + +## Sonarr + +In `host1.conf`. + +Reachable from: + +- Scheduler tab → **Sonarr Cleanup** → Config → *Sonarr* +- Scheduler tab → **Sonarr Classification Scan** → Config → *Sonarr* +- Scheduler tab → **Playback Aware Sonarr Discovery** → Config → *Sonarr* +- Scheduler tab → **Sonarr Tvdb Removed** → Config → *Sonarr* +- Scheduler tab → **Emby To Sonarr Sync** → Config → *Sonarr* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_SONARR_URL` | a text box | in this section | — | +| `HOST1_SONARR_API_KEY` | a masked box with a **Show** button | in this section | — | +| `HOST1_SONARR_TV_ROOT` | a text box | in this section | — | +| `HOST1_SONARR_GENERAL_ROOT` | a text box | in this section | rootFolderPath literal for the general root — target for reverse-kids-leak moves | +| `HOST1_SONARR_KIDS_ROOT` | a text box | in this section | rootFolderPath literal, as reported by Sonarr API | +| `HOST1_SONARR_ANIME_ROOT` | a text box | in this section | — | +| `HOST1_SONARR_DOWNLOAD_DIR` | a text box | in this section | — | +| `HOST1_SONARR_DOWNLOAD_CONTAINER_DIR` | a text box | in this section | — | +| `HOST1_SONARR_PATH_MAP` | a list, one entry per line | in this section | — | + +## Storage mode + +In `host1.conf`. + +No single page shows this section. Individual settings below carry their own route. + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_STORAGE_MODE_INTERNAL` | a switch | Settings tab | Controls where Varaverk stores scripts, conf, and state files. true = internal NVMe/SSD — /boot/config/plugins/varaverk (write-safe, git-direct) false = USB flash boot — /mnt/user/appdata/Varaverk (preserves flash lifetime) Auto-detected from boot device transport on first setup. To change: Settings → Storage → Migrate. | + +## Weekly Sync Shares + +In `host1.conf`. + +Route: Scheduler tab → **Weekly Sync Maintenance** → Config → *Weekly Sync Shares* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_WEEKLY_SYNC_SHARES` | a list, one entry per line | in this section | Appdata shares synced during the weekly maintenance window. Profiles (emby, critical-data) drive container stops — define in master.conf. | + +## ZFS Report + +In `host1.conf`. + +Route: Scheduler tab → **Zfs Memory Snapshot** → Config → *ZFS Report* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `HOST1_ZFS_REPORT_IGNORE_POOLS` | a list, one entry per line | in this section | — | + +## AI Conf Write Access + +In `master.conf`. + +Route: AI tab → Settings → Configuration → *AI Conf Write Access* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_CONF_WRITE_ENABLED` | a switch | in this section | Separate switch from AI_ENABLED, off by default, and an explicit key whitelist. Never paths, never credentials, never a container name. An empty whitelist means no writes regardless of the toggle. | +| `AI_CONF_WRITE_KEYS` | a list, one entry per line | in this section | — | + +## AI Feature Toggles + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Ai Token Sync** → Config → *AI Feature Toggles* +- AI tab → Settings → Configuration → *AI Feature Toggles* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_ASSIST_REPORTS` | a switch | in this section | tier 1 — digest / coffee report narration | +| `AI_ASSIST_WATCHDOG` | a switch | in this section | tier 2 — context on a flagged condition | +| `AI_ASSIST_DISCOVERY` | a switch | in this section | tier 2 — discovery / classification judgement calls | +| `AI_ASSIST_CLEANUP` | a switch | in this section | tier 2 — orphan and stuck-import triage | +| `AI_ASSIST_ONBOARD` | a switch | in this section | tier 3 — onboarding / settings assistance | + +## AI Learned Memory + +In `master.conf`. + +Route: AI tab → Settings → Configuration → *AI Learned Memory* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_MEMORY_LEARN_ENABLED` | a switch | in this section | Costs nothing while off: the instruction that asks for a candidate is only added to the prompt when this is true, so a disabled feature is genuinely absent rather than merely ignored. | +| `AI_MEMORY_LEARN_AUTO_ACCEPT` | a switch | in this section | Writes accepted candidates without asking. Cannot outrank its parent — with proposing off this does nothing. Leave it false until the proposals have proven good for a while. | + +## AI Master Switch + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Ai Index** → Config → *AI Master Switch* +- Scheduler tab → **Ai Query** → Config → *AI Master Switch* +- Scheduler tab → **Ai Repair Sweep** → Config → *AI Master Switch* +- AI tab → Settings → Configuration → *AI Master Switch* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_ENABLED` | a switch | Settings tab | Fail-closed: anything other than the literal "true" means off. | +| `AI_CONNECT_TIMEOUT` | a number box, in seconds | in this section | seconds — probe when resolving which node has Ollama | +| `AI_REQUEST_TIMEOUT` | a number box, in seconds | in this section | seconds — must clear a cold model load | +| `AI_RESOLVE_CACHE_TTL` | a number box, in seconds | in this section | seconds — don't re-probe the mesh every invocation | +| `AI_MAX_RETRIES` | a number box | in this section | AI is enhancement; do not retry hard | + +## AI Memory + +In `master.conf`. + +Route: AI tab → Settings → Configuration → *AI Memory* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_MEMORY_ASSISTED_FILE` | a text box | in this section | AI_MEMORY_FILE is the pre-split name and is still honoured: while the assisted file does not exist, the legacy path is read instead, so an upgrade loses nothing. | +| `AI_MEMORY_LEARNED_FILE` | a text box | in this section | — | +| `AI_MEMORY_FILE` | a text box | in this section | — | +| `AI_MEMORY_ASSISTED_PROFILES` | a text box | in this section | Which profiles each slot is given. "*" is all of them. Narrowing is how a General Chat question about bash syntax stops carrying this machine's PCIe topology and disk serials. | +| `AI_MEMORY_LEARNED_PROFILES` | a text box | in this section | — | +| `AI_MEMORY_MAX_CHARS` | a number box | in this section | ~1000 tokens — truncated with a notice if exceeded | +| `AI_MEMORY_LEARNED_MAX_CHARS` | a number box | in this section | A ceiling on the learned slot alone, well under the total. A store that grows on its own would otherwise end up occupying the whole budget, and the operator's own memory is what would get truncated away — the exact inversion of which one matters. | + +## AI Repair + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Ai Repair Sweep** → Config → *AI Repair* +- AI tab → Settings → Configuration → *AI Repair* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_REPAIR_ENABLED` | a switch | in this section | AI_REPAIR_AUTOFIX_ENABLED is what allows a value to be written unattended, and only ever a value a probe has answered on. Never a toggle: whether something should be switched on is a decision about intent, and a probe cannot prove intent the way it can prove a port answers. | +| `AI_REPAIR_AUTOFIX_ENABLED` | a switch | in this section | — | + +## AI Repair Findings + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Ai Repair Sweep** → Config → *AI Repair Findings* +- AI tab → Settings → Configuration → *AI Repair Findings* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_FINDING_RETAIN_DAYS` | a number box | in this section | Misconfigurations found in this installation, as opposed to defects in Varaverk — those go to ai_bugs. A finding is open until the configuration is right, and closes itself when the probe that proved the fault starts passing. Closed ones are kept for a while, because "this happened before and here is what fixed it" is worth more than the disk. Open findings are never pruned: an unresolved probl… | +| `AI_PROBE_TIMEOUT` | a number box, in seconds | in this section | Seconds a single probe may take. Nothing is written to conf that has not answered a probe, so this is the budget for proving a candidate — kept short because a sweep may try several, and an address worth switching to answers quickly or is not worth switching to. | +| `AI_REPAIR_NOTIFY_ENABLED` | a switch | in this section | On by default, unlike the two switches above. Those gate reading and writing, which are things to be trusted first. This gates telling someone, which is the reason for having looked. | + +## AI Repair: what it reads + +In `master.conf`. + +Route: AI tab → Settings → Configuration → *AI Repair: what it reads* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_REPAIR_SYSLOG_ENABLED` | a switch | in this section | Both are bounded by time (since the last pass) and by a line cap, so a flood costs one pass. Tools/ai_log_check.sh replays this host's real logs against the patterns — run it after changing any of them. | +| `AI_REPAIR_SYSLOG_MAX_LINES` | a number box | in this section | — | +| `AI_REPAIR_CONTAINER_LOGS_ENABLED` | a switch | in this section | — | +| `AI_REPAIR_CONTAINER_LOG_LINES` | a number box | in this section | — | + +## AI Retrieval Index + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Ai Index** → Config → *AI Retrieval Index* +- Scheduler tab → **Ai Query** → Config → *AI Retrieval Index* +- AI tab → Settings → Configuration → *AI Retrieval Index* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_INDEX_DB` | a text box | in this section | Only git-tracked files are ever indexed. Configurations/, State_Files/ and data/ are gitignored, which is what makes it structurally impossible for a credential to reach the index: the files holding them were never in the repo. Do not "improve" this to a filesystem walk — an embedded secret cannot be rotated out of a vector. | +| `AI_INDEX_BATCH` | a number box | in this section | chunks per embed request | +| `AI_INDEX_ON_PULL` | a switch | in this section | re-index after a git pull that changed tracked files | +| `AI_SEARCH_K` | a number box | in this section | chunks retrieved per query | +| `AI_SEARCH_PER_FILE` | a number box | in this section | cap per file so one document cannot fill the context | + +## AI Stored Conversations + +In `master.conf`. + +Route: AI tab → Settings → Configuration → *AI Stored Conversations* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_CHAT_HISTORY_MAX` | a number box, in conversations, 1–50 | in this section | conversations kept — clamped to 1-50 | + +## AI Token Accounting + +In `master.conf`. + +Route: AI tab → Settings → Configuration → *AI Token Accounting* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_TOKEN_DB` | a text box | in this section | Retention is by row count rather than age: pruning is considered only when the file passes a size threshold, so an ordinary turn costs one stat() and an append. | +| `AI_TOKEN_RETAIN_ROWS` | a number box | in this section | oldest rows dropped past this — years of ordinary use | +| `AI_TOKEN_SYNC_ENABLED` | a switch | in this section | AI/ai_token_sync.sh pulls each partner's ledger into the tmpfs cache the tab reads, so the fleet total is a fleet total. Same trick conf_sync.sh uses for partner confs, minus the push: nothing here is needed by anyone else, so the reader fetches its own data and controls its own freshness. An unreachable partner is a quiet skip, not a warning — a partner is expected to be down for long stretche… | + +## AI Web Search + +In `master.conf`. + +Route: AI tab → Settings → Configuration → *AI Web Search* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `AI_WEB_SEARCH_ENABLED` | a switch | in this section | Provider: degoog \| searxng \| brave \| tavily degoog self-hosted, no key — set HOST*_DEGOOG_URL. Aggregates several engines and returns them merged. The default, and the only one verified against a live service here searxng self-hosted, no key, no third party — set HOST*_SEARXNG_URL, and enable format: [json] in its own settings.yml, which the default image ships with off brave HOST*_WEB_SEARC… | +| `AI_WEB_SEARCH_PROVIDER` | a text box | in this section | — | +| `AI_WEB_SEARCH_RESULTS` | a number box | in this section | — | +| `AI_WEB_SEARCH_TIMEOUT` | a number box | in this section | — | + +## Arr Cleanup + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Radarr Cleanup** → Config → *Arr Cleanup* +- Scheduler tab → **Lidarr Cleanup** → Config → *Arr Cleanup* +- Scheduler tab → **Sonarr Cleanup** → Config → *Arr Cleanup* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `SONARR_VERSION_MAJOR` | a number box | in this section | API versions — update MAJOR version here when script is updated for a new arr version: Sonarr v4 → /api/v3/series → /api/v3/episodefile?seriesId=X Radarr v6 → /api/v3/movie → /api/v3/moviefile?movieId=X Lidarr v3 → /api/v1/artist → /api/v1/trackFile?artistId=X | +| `RADARR_VERSION_MAJOR` | a number box | in this section | — | +| `LIDARR_VERSION_MAJOR` | a number box | in this section | — | +| `ARR_KIDS_PROFILE_NAME` | a text box | in this section | arr_profile_enforcer.sh — quality profile names by root folder type Root folder paths containing "kids" or "anime" → ARR_KIDS_PROFILE_NAME All other root folders → ARR_*_DEFAULT_PROFILE | +| `ARR_SONARR_DEFAULT_PROFILE` | a text box | in this section | — | +| `ARR_RADARR_DEFAULT_PROFILE` | a text box | in this section | — | +| `LIDARR_RELEASE_FIXER_ENABLED` | a switch | in this section | set false to disable without removing from job list | +| `LIDARR_LOCK_WARN_AGE` | a number box | in this section | 1hr — large libraries take time, not stuck | +| `LIDARR_ORPHAN_AGE` | a number box, in days | in this section | days — files must be older than this before eligible for deletion | +| `LIDARR_MAX_DELETE_GB` | a number box | in this section | require --i-know-what-im-doing if deletion exceeds this | +| `LIDARR_MIN_TRACKED_PCT` | a number box | in this section | abort if tracked count drops below this % of last run | +| `LIDARR_TRACKED_COUNT_FILE` | a text box | in this section | — | +| `LIDARR_IMPORT_SCAN_TIMEOUT` | a number box, in seconds | in this section | seconds to wait for pre-flight import scan | +| `LIDARR_CACHE_FILE` | a text box | in this section | Lidarr tracked-data cache — shared by lidarr_cleanup.sh, lidarr_duplicate_artist_cleanup.sh, lidarr_missing_art.sh, lidarr_release_fixer.sh, and arr_cache_prefill.sh. See lidarr_get_tracked_data() in common.sh for the fresh/stale/rescan-active branching logic. | +| `LIDARR_RESCAN_DURATION_DB` | a text box | in this section | — | +| `LIDARR_CACHE_MAX_AGE_DAYS` | a number box | in this section | force a live refresh (or rescan-aware wait) past this age | +| `ARR_PREFILL_WAIT_MINUTES` | a number box | in this section | array-start prefill: how long to retry reaching each arr | +| `LIDARR_EXTENSIONS` | a list, one entry per line | in this section | — | +| `LIDARR_PROTECTED_PATTERNS` | a list, one entry per line | in this section | — | +| `LIDARR_ART_MIN_SIZE` | a number box | in this section | bytes — reject downloads smaller than this | +| `LIDARR_ART_MAX_PARALLEL` | a number box | in this section | concurrent background download jobs | +| `LIDARR_ART_RETRIES` | a number box | in this section | download retry attempts per image | +| `LIDARR_ART_SLEEP_BETWEEN` | a text box | in this section | seconds between fanart.tv API calls | +| `LIDARR_ART_RECHECK_DAYS` | a number box, in days | in this section | days before re-querying art that upstream didn't have | +| `LIDARR_ART_MISS_CACHE` | a text box | in this section | negative cache — art upstream has never had | +| `LIDARR_DISCOVERY_THRESHOLD` | a number box | in this section | score to accept candidate (0-100) | +| `LIDARR_DISCOVERY_LOOKBACK_DAYS` | a number box | in this section | Emby play history window in days | +| `LIDARR_DISCOVERY_MIN_PLAYS` | a number box | in this section | min plays in window before evaluating an artist | +| `LIDARR_DISCOVERY_USER_CAP_PCT` | a number box | in this section | max % any single user can contribute to play score (prevents one listener dominating) | +| `LIDARR_DISCOVERY_MAX_ADDS` | a number box | in this section | max artists to add per run — quality over bulk | +| `LIDARR_DISCOVERY_REJECT_COOLDOWN` | a number box, in days | in this section | days before re-evaluating a rejected artist | +| `LIDARR_DISCOVERY_HISTORY` | a text box | in this section | — | +| `SONARR_DISCOVERY_THRESHOLD` | a number box | in this section | score to accept candidate (0-100) | +| `SONARR_DISCOVERY_LOOKBACK_DAYS` | a number box | in this section | Emby episode play history window in days (shorter than movies — TV watched more frequently) | +| `SONARR_DISCOVERY_MAX_SEEDS` | a number box | in this section | max seed series from Stage 1 | +| `SONARR_DISCOVERY_MAX_ADDS` | a number box | in this section | max shows to add per run — TV is a larger commitment than movies | +| `SONARR_DISCOVERY_MIN_VOTE_COUNT` | a number box | in this section | min TMDB votes (TV has fewer votes than movies at same popularity) | +| `SONARR_DISCOVERY_MIN_RATING` | a number box | in this section | min TMDB vote_average × 10 (65 = 6.5/10) | +| `SONARR_DISCOVERY_REJECT_COOLDOWN` | a number box, in days | in this section | days before re-evaluating a rejected show | +| `SONARR_DISCOVERY_USER_EPISODE_CAP` | a number box | in this section | max episodes any one user contributes to seed volume score | +| `SONARR_DISCOVERY_MONITOR_MODE` | a dropdown (all, future, first, latest, none) | in this section | Sonarr monitor mode on add: all \| future \| first \| latest \| none | +| `SONARR_DISCOVERY_HISTORY` | a text box | in this section | — | +| `RADARR_DISCOVERY_THRESHOLD` | a number box | in this section | score to accept candidate (0-100) — lower than Lidarr since diverse seeds rarely overlap | +| `RADARR_DISCOVERY_LOOKBACK_DAYS` | a number box | in this section | Emby watch history window in days (movies rewatched less often) | +| `RADARR_DISCOVERY_MAX_SEEDS` | a number box | in this section | max seed movies from Stage 1 | +| `RADARR_DISCOVERY_MAX_ADDS` | a number box | in this section | max movies to add per run | +| `RADARR_DISCOVERY_MIN_VOTE_COUNT` | a number box | in this section | min TMDB votes to be considered a candidate | +| `RADARR_DISCOVERY_MIN_RATING` | a number box | in this section | min TMDB vote_average × 10 (60 = 6.0/10) | +| `RADARR_DISCOVERY_REJECT_COOLDOWN` | a number box, in days | in this section | days before re-evaluating a rejected movie | +| `RADARR_DISCOVERY_SEED_LIBRARIES` | a list, one entry per line | in this section | — | +| `RADARR_DISCOVERY_HISTORY` | a text box | in this section | — | +| `SONARR_EMBY_LIBRARIES` | a list, one entry per line | in this section | Emby → arr sync library allowlists Only these Emby library names will be considered by the sync tools. Names must match exactly as shown in Emby > Dashboard > Libraries. | +| `RADARR_EMBY_LIBRARIES` | a list, one entry per line | in this section | — | +| `SONARR_ORPHAN_AGE` | a number box, in days | in this section | days — files must be older than this before eligible for deletion | +| `SONARR_MAX_DELETE_GB` | a number box | in this section | require --i-know-what-im-doing if deletion exceeds this | +| `SONARR_MIN_TRACKED_PCT` | a number box | in this section | abort if tracked count drops below this % of last run | +| `SONARR_TRACKED_COUNT_FILE` | a text box | in this section | — | +| `SONARR_IMPORT_SCAN_TIMEOUT` | a number box, in seconds | in this section | seconds to wait for pre-flight import scan | +| `SONARR_MOVE_POLL_TIMEOUT` | a number box, in seconds | in this section | seconds to wait for a single async MoveSeries command to | +| `CORRUPTION_SCAN_STATE_FILE` | a text box | in this section | clean-file skip-cache | +| `CORRUPTION_SCAN_STRIKES_FILE` | a text box | in this section | consecutive corrupt-detection counts, keyed by host path | +| `CORRUPTION_SCAN_STRIKE_LIMIT` | a number box, in consecutive | in this section | consecutive corrupt detections (across separate scan runs) | +| `SONARR_EXTENSIONS` | a list, one entry per line | in this section | — | +| `SONARR_PROTECTED_PATTERNS` | a list, one entry per line | in this section | — | +| `RADARR_ORPHAN_AGE` | a number box, in days | in this section | days — files must be older than this before eligible for deletion | +| `RADARR_MAX_DELETE_GB` | a number box | in this section | require --i-know-what-im-doing if deletion exceeds this | +| `RADARR_MIN_TRACKED_PCT` | a number box | in this section | abort if tracked count drops below this % of last run | +| `RADARR_TRACKED_COUNT_FILE` | a text box | in this section | — | +| `RADARR_IMPORT_SCAN_TIMEOUT` | a number box, in seconds | in this section | seconds to wait for pre-flight import scan | +| `RADARR_MOVE_POLL_TIMEOUT` | a number box, in seconds | in this section | seconds to wait for a single async MoveMovie command to | +| `RADARR_EXTENSIONS` | a list, one entry per line | in this section | — | +| `RADARR_PROTECTED_PATTERNS` | a list, one entry per line | in this section | — | +| `RADARR_DROPPED_ADD_EXCLUSION` | a switch | in this section | add removed movies to import exclusion list | +| `SONARR_DROPPED_ADD_EXCLUSION` | a switch | in this section | add removed series to import exclusion list | + +## Arr Content Classification (radarr/sonarr_classification_scan.sh) + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Radarr Classification Scan** → Config → *Arr Content Classification (radarr/sonarr_classification_scan.sh)* +- Scheduler tab → **Sonarr Classification Scan** → Config → *Arr Content Classification (radarr/sonarr_classification_scan.sh)* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `RADARR_ANIME_STUDIOS` | a list, one entry per line | in this section | — | +| `RADARR_KIDS_STUDIOS` | a list, one entry per line | in this section | — | +| `RADARR_JUNK_MIN_VOTES` | a number box | in this section | — | +| `SONARR_ANIME_NETWORKS` | a list, one entry per line | in this section | — | +| `SONARR_KIDS_NETWORKS` | a list, one entry per line | in this section | — | + +## Arr Failed/Stalled Recovery + +In `master.conf`. + +Route: Scheduler tab → **Arrs Failed Stalled Recovery** → Config → *Arr Failed/Stalled Recovery* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `ARR_IMPORT_RECOVERY_AGE` | a number box, in hours | Arrs tab | hours — skip items newer than this | +| `ARR_RECOVERY_MAX_ATTEMPTS` | a number box, in consecutive | in this section | consecutive failures before an item is flagged chronic | +| `ARR_SMART_IMPORT_ENABLED` | a switch | in this section | importBlocked items: try importing a clean same-language | +| `ARR_SMART_IMPORT_PREFERRED_LANGUAGE` | a text box | in this section | only treated as a match/upgrade if the | + +## Arr Full Library Rescan + +In `master.conf`. + +Route: Scheduler tab → **Arr Full Rescan** → Config → *Arr Full Library Rescan* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `ARR_FULL_RESCAN_TIMEOUT` | a number box, in seconds | in this section | seconds per arr — a full-library walk, not a single release | + +## Array Start + +In `master.conf`. + +Route: Scheduler tab → **Array Started** → Config → *Array Start* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `ARRAY_START_SCRIPTS` | a list, one entry per line | in this section | Scripts launched by array_started.sh when the array comes online. Launched in order — each as a background process. One-shot scripts (ramdisk, syslog, fpm, inotify, network) run and exit naturally. Continuous scripts (failover) run until array stops. Watchdogs (resource_watchdog, docker_watchdog, system_watchdog) are cronned via watchdog_orchestrator.sh — NOT launched here. | + +## Array Stop + +In `master.conf`. + +Route: Scheduler tab → **Array Stopping** → Config → *Array Stop* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `ARRAY_STOP_SCRIPTS` | a list, one entry per line | in this section | Scripts run by array_stopping.sh for a planned shutdown — stops everything cleanly in order. Run sequentially (foreground) — each must complete before the next starts. Order matters: user scripts first (prevents new ops), then data movement, then containers. | + +## Backup Verify + +In `master.conf`. + +Route: Scheduler tab → **Backup Verify** → Config → *Backup Verify* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `BACKUP_VERIFY_SAMPLE` | a number box | in this section | random files to check per share | +| `BACKUP_VERIFY_MIN_SIZE` | a text box | in this section | minimum file size to include in sample | + +## Bandwidth Monitor + +In `master.conf`. + +Route: Scheduler tab → **Bandwidth Monitor** → Config → *Bandwidth Monitor* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `BANDWIDTH_LOG` | a text box | in this section | Called automatically by rsync.sh after each sync — one bounded write per run. Tracks transfer size, duration and profile per sync for weekly summary reporting. | +| `BANDWIDTH_LOG_RETENTION` | a number box, in days | in this section | days before old entries purged | +| `BANDWIDTH_WARN_GB` | a number box | Rsync tab | flag syncs larger than this in weekly report | +| `ARR_CLEANUP_STATS` | a text box | in this section | lidarr/sonarr/radarr orphan stats | +| `ARR_RECOVERY_STATS` | a text box | in this section | blocklist + re-search stats | +| `ARR_RECOVERY_FAILURE_COUNTS` | a text box | in this section | per-item chronic-failure tracking | + +## Certificate Monitor + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Cert Monitor** → Config → *Certificate Monitor* +- Scheduler tab → **Weekly Health Digest** → Config → *Certificate Monitor* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `CERT_WARN_DAYS` | a number box | in this section | warn when cert expires within this many days | +| `CERT_CRIT_DAYS` | a number box | in this section | critical alert within this many days | +| `CERT_TIMEOUT` | a number box, in seconds | in this section | seconds per domain before giving up | + +## Clear Logs + +In `master.conf`. + +Route: Scheduler tab → **Clear Logs** → Config → *Clear Logs* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `LOG_FILES` | a list, one entry per line | in this section | Size threshold approach — only clear if log exceeds threshold. Avoids destroying useful recent diagnostic context when logs are small. LOG_MIN_SIZE_MB: skip clearing if log is under this size (not worth clearing) LOG_DOCKER_MAX_MB: clear a container log only if it exceeds this size Docker logs grow fastest on active containers (Emby, SABnzbd, Sonarr) 100MB per container × 30 containers = 3GB be… | +| `LOG_MIN_SIZE_MB` | a number box | in this section | skip system log if under this size (already small) | +| `LOG_DOCKER_MAX_MB` | a number box | in this section | clear Docker container log only if over this size (MB) | + +## Critical Sync Maintenance + +In `master.conf`. + +Route: Scheduler tab → **Critical Sync Maintenance** → Config → *Critical Sync Maintenance* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `CRITICAL_MAINTENANCE_SCRIPTS` | a list, one entry per line | in this section | critical_sync_maintenance.sh runs every 30 minutes. Order: CRITICAL_MAINTENANCE_SCRIPTS (jobs) → CRITICAL_SYNC_SHARES (rsync) → partnership --check partnership --check always runs last regardless of rsync gate. Comment out entries to disable without removing. | + +## Daily Sync Maintenance + +In `master.conf`. + +Route: Scheduler tab → **Daily Sync Maintenance** → Config → *Daily Sync Maintenance* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `DAILY_MAINTENANCE_SCRIPTS` | a list, one entry per line | in this section | daily_sync_maintenance.sh runs media share sync first, then iterates DAILY_MAINTENANCE_SCRIPTS for all jobs. Schedule: 0 1 * * * (1am daily) | +| `DAILY_CONTAINER_UPDATES` | a switch | in this section | Pull latest images for DAILY_RESTART_CONTAINERS before the daily restart. Containers keep running during pull — no extra downtime. Set false to skip updates while still running the daily restart. | + +## Docker Watchdog + +In `master.conf`. + +Route: Scheduler tab → **Docker Watchdog** → Config → *Docker Watchdog* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `WATCHDOG_STATE_FILE` | a text box | in this section | Strike state file — /tmp resets on reboot (correct — no stale strikes after reboot) | +| `SOFT_CPU_THRESHOLD` | a number box | in this section | warn at this % of total system CPU | +| `HARD_CPU_THRESHOLD` | a number box | in this section | strike at this % of total system CPU | +| `CPU_FAIL_LIMIT` | a number box, in consecutive | in this section | consecutive hard CPU strikes before container restart | +| `SOFT_MEM_THRESHOLD` | a number box | in this section | Memory soft threshold — warn when container reaches this % of its hard limit | +| `RESP_FAIL_LIMIT` | a number box, in consecutive | in this section | consecutive failed checks before restart | +| `CURL_TIMEOUT` | a number box, in seconds | in this section | seconds per check before timeout | +| `WATCHDOG_SCAN_ALL` | a switch | in this section | false = only WATCHDOG_CONTAINERS + required containers | +| `WATCHDOG_RESTART_UNHEALTHY` | a switch | in this section | Individual Tier 2 check toggles | +| `WATCHDOG_RESTART_DEAD` | a switch | in this section | — | +| `WATCHDOG_RESTART_CRASHED` | a switch | in this section | — | +| `WATCHDOG_NOTIFY_OOM` | a switch | in this section | — | +| `WATCHDOG_NOTIFY_CRASHLOOP` | a switch | in this section | — | +| `WATCHDOG_CRASH_LIMIT` | a number box | in this section | RestartCount above this = critical crash loop | +| `WATCHDOG_REQUIRED_STRIKE_LIMIT` | a number box | in this section | Required-container strikes — consecutive down-checks before docker_watchdog.sh attempts a restart | +| `WATCHDOG_STARTUP_GRACE` | a number box, in seconds | in this section | seconds after boot before watchdog acts on failures | +| `WATCHDOG_CONTAINER_RESTART_LIMIT` | a number box | in this section | Restart loop protection — prevents watchdog from endlessly restarting a broken container | +| `WATCHDOG_CONTAINER_RESTART_WINDOW` | a number box | in this section | rolling window in hours | +| `WATCHDOG_CONTAINER_RESTART_LOG` | a text box | in this section | — | +| `WATCHDOG_BATCH_NOTIFY` | a switch | in this section | Notification batching — one summary per cycle instead of one ping per event | +| `WATCHDOG_DAEMON_TIMEOUT` | a number box, in seconds | in this section | seconds — timeout for all docker commands | +| `WATCHDOG_DAEMON_STRIKE_LIMIT` | a number box, in consecutive | in this section | consecutive failed checks before restart attempt | +| `WATCHDOG_DAEMON_RESTART_WAIT` | a number box, in seconds | in this section | seconds to wait after restart before verifying | +| `WATCHDOG_CHECK_APPDATA` | a switch | in this section | HOST*_WATCHDOG_APPDATA_SIZES (in host*.conf) suppresses growth warnings for a container until its dir exceeds the configured ceiling. Only needed when a container legitimately has large stable data and you want to guarantee it never triggers a false alarm. | +| `WATCHDOG_APPDATA_PATHS` | a list, one entry per line | in this section | — | +| `WATCHDOG_APPDATA_GROWTH_GB` | a number box | in this section | flag containers growing more than this per cycle | +| `WATCHDOG_APPDATA_LOG_MAX_GB` | a number box | in this section | flag *.log files exceeding this size (absolute) | +| `WATCHDOG_APPDATA_TRUNCATE_LOGS` | a switch | in this section | set true to auto-truncate oversized *.log files on action cycle | +| `WATCHDOG_APPDATA_STRIKE_LIMIT` | a number box | in this section | cycles before action fires (matches existing watchdog pattern) | +| `WATCHDOG_APPDATA_GROWTH_FILE` | a text box | in this section | — | +| `STORAGE_WATCHDOG_STATE_FILE` | a text box | in this section | — | + +## Download Orphan Cleaner (arr_download_orphan_cleaner.sh) + +In `master.conf`. + +Route: Scheduler tab → **Arr Download Orphan Cleaner** → Config → *Download Orphan Cleaner (arr_download_orphan_cleaner.sh)* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `DOWNLOAD_ORPHAN_CLEANER_ENABLED` | a switch | in this section | Weekly sweep of the SABnzbd Completed folders Sonarr/Radarr import from — deletes junk and parse-verified already-in-library leftovers the arr queue no longer references, triggers import scans for anything the library is actually missing. Built 2026-07-26 after 755G of orphaned completed downloads (accumulating since 2022) filled HOST1's cache pool to 89%. Per-host dirs: HOST*_SONARR_DOWNLOAD_D… | +| `DOWNLOAD_ORPHAN_AGE` | a number box, in days | in this section | days — entries younger than this may be mid-import, never touched | +| `DOWNLOAD_ORPHAN_MIN_VIDEO_MB` | a number box | in this section | no video file above this = junk (par2 debris, samples, dead archives) | +| `DOWNLOAD_ORPHAN_MAX_DELETE_GB` | a number box | in this section | abort delete pass over this — a partial queue fetch would classify | + +## Downloaders Reset + +In `master.conf`. + +Route: Scheduler tab → **Downloaders Reset** → Config → *Downloaders Reset* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `DOWNLOADER_RETENTION_DAYS` | a number box, in days | in this section | days — purge history older than this | +| `QBIT_FAILSAFE_MIN_DAYS` | a number box, in days | in this section | days — minimum age before failsafe deletion | +| `QBIT_FAILSAFE_MIN_RATIO` | a number box | in this section | 0 = age only, no ratio requirement | + +## Emby Session Report + +In `master.conf`. + +Route: Scheduler tab → **Emby Session Report** → Config → *Emby Session Report* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `EMBY_REPORT_DAYS` | a number box, in days | in this section | days to include in the report period | +| `EMBY_REPORT_TOP_N` | a number box | in this section | number of top content items to show | + +## FALLBACK + +In `master.conf`. + +No single page shows this section. Individual settings below carry their own route. + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `EXTERNAL_IP` | a text box | — | — | +| `FALLBACK_CHECK_INTERVAL` | a number box, in seconds | Fallback tab | seconds between fallback state checks | +| `FALLBACK_HANDBACK_STRIKES` | a number box, in consecutive | Fallback tab | consecutive healthy checks before initiating handback (3×30s = 90s) | +| `FALLBACK_STATE_FILE` | a text box | — | — | +| `FALLBACK_ENABLED` | a switch | Fallback tab | HOST2 back online | +| `FALLBACK_PARTNERSHIP_SUSPEND_AFTER` | a number box, in minutes | Fallback tab | minutes without active partnership before suspending | + +## Failover Test + +In `master.conf`. + +Route: Scheduler tab → **Fallback Test** → Config → *Failover Test* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `FALLBACK_TEST_BLOCK_WAIT` | a number box, in seconds | in this section | seconds to wait after blocking connectivity | +| `FALLBACK_TEST_HANDBACK_WAIT` | a number box, in seconds | in this section | seconds to wait before initiating handback | + +## Health Digest + +In `master.conf`. + +Route: Scheduler tab → **Weekly Health Digest** → Config → *Health Digest* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `DIGEST_PROFILE` | a dropdown (always, smart, weekly) | in this section | always \| smart \| weekly | +| `DIGEST_DAY` | a text box | in this section | — | +| `DIGEST_SMART_ON_WATCHDOG` | a switch | in this section | send if any watchdog strikes are active | +| `DIGEST_SMART_ON_FALLBACK` | a switch | in this section | send if fallback state is not NORMAL | +| `DIGEST_SMART_ON_CERT_WARN` | a switch | in this section | send if any cert is under CERT_WARN_DAYS | +| `DIGEST_SMART_ON_BANDWIDTH` | a switch | in this section | send if any transfer exceeded BANDWIDTH_WARN_GB | + +## Intermediate Sync Maintenance + +In `master.conf`. + +Route: Scheduler tab → **Intermediate Sync Maintenance** → Config → *Intermediate Sync Maintenance* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `INTERMEDIATE_MAINTENANCE_SCRIPTS` | a list, one entry per line | in this section | — | + +## LOGGING + +In `master.conf`. + +No single page shows this section. Individual settings below carry their own route. + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `ENABLE_LOGGING` | a switch | Settings tab | Controls verbose [LOG] output across all scripts. true = show detailed [LOG] lines — useful for debugging or first-time setup false = show only user-facing output — cleaner for scheduled runs | + +## Media Cleaner + +In `master.conf`. + +Route: Scheduler tab → **Media Cleaner** → Config → *Media Cleaner* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `ANIME_FILE_PATTERNS` | a list, one entry per line | in this section | — | +| `MEDIA_FILE_PATTERNS` | a list, one entry per line | in this section | — | + +## Media Permissions + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Media Shares Permissions** → Config → *Media Permissions* +- Scheduler tab → **Bulk Permissions Repair** → Config → *Media Permissions* +- Scheduler tab → **Trailer Folder Migration** → Config → *Media Permissions* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `PERMISSIONS_DIR_MODE` | a text box | in this section | directories — traverse + list, no world-write | +| `PERMISSIONS_FILE_MODE` | a text box | in this section | files — group read/write, no execute | +| `PERMISSIONS_OWNER` | a text box | in this section | — | + +## Monthly Maintenance + +In `master.conf`. + +Route: Scheduler tab → **Monthly Maintenance** → Config → *Monthly Maintenance* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `MONTHLY_MAINTENANCE_SCRIPTS` | a list, one entry per line | in this section | monthly_maintenance.sh fires only when BOTH gates pass: 1. Server uptime >= MONTHLY_UPTIME_THRESHOLD_DAYS days 2. Last run was >= MONTHLY_RUN_INTERVAL_DAYS days ago (or never run) Cron: 0 0 15 * * (15th of the month, midnight — script self-gates, so a spare run is safe) NOT in WATCHDOG_ORCHESTRATOR_SCRIPTS — has its own cron entry. Add scripts that require a long-stable settled system — scrubs,… | +| `MONTHLY_UPTIME_THRESHOLD_DAYS` | a number box | in this section | minimum uptime in days before maintenance fires | +| `MONTHLY_RUN_INTERVAL_DAYS` | a number box | in this section | minimum days since last run before running again | +| `MONTHLY_LAST_RUN_FILE` | a text box | in this section | — | + +## Mover + +In `master.conf`. + +Route: Scheduler tab → **Mover Stop** → Config → *Mover* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `MOVER_STOP_TIMEOUT` | a number box, in seconds | in this section | Seconds to wait before mover_stop.sh sends SIGTERM to the mover process. Gives mover time to finish current file transfer before being interrupted. | + +## NOTIFICATIONS + +In `master.conf`. + +No single page shows this section. Individual settings below carry their own route. + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `NOTIFY_UNRAID` | a switch | Settings tab | unRAID native notification system — integrates with the bell icon in the WebGUI. normal = job completed successfully / warning = something failed or needs attention | +| `WEBHOOK_PORT` | a number box | — | Upgrade webhook — standalone PHP listener that receives OnUpgrade events from Sonarr/Radarr/Lidarr and immediately pushes the upgraded file to all mesh nodes. Bypasses Unraid nginx auth — the secret in the URL is the only gate. Run Tools/webhook_setup.sh once to register the connection in each arr. WEBHOOK_PORT 0 disables the listener. | +| `WEBHOOK_SECRET` | a masked box with a **Show** button | — | auto-generated on first start if empty | + +## Network Watchdog + +In `master.conf`. + +Route: Scheduler tab → **Network Watchdog** → Config → *Network Watchdog* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `NETWORK_WATCHDOG_ENABLED` | a switch | in this section | Services-layer connectivity — internet reachability, DDNS sync, Tailscale, NPM proxy. Host-specific values (domain, container, NPM URL) live in host*.conf. | +| `NETWORK_WATCHDOG_INTERNET_URL` | a text box | in this section | — | +| `NETWORK_WATCHDOG_INTERNET_TIMEOUT` | a number box | in this section | — | +| `NETWORK_WATCHDOG_CHECK_TAILSCALE` | a switch | in this section | — | +| `NETWORK_WATCHDOG_NPM_TIMEOUT` | a number box | in this section | — | +| `NETWORK_WATCHDOG_NPM_STRIKE_LIMIT` | a number box | in this section | — | +| `NETWORK_WATCHDOG_NPM_STATE_FILE` | a text box | in this section | — | + +## OOM Bypass Settings + +In `master.conf`. + +Route: Scheduler tab → **Stability Watchdog** → Config → *OOM Bypass Settings* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `SYS_WATCHDOG_OOM_LIMIT` | a number box | in this section | OOM kills in one cycle to trigger bypass | + +## PARTNERSHIP + +In `master.conf`. + +Route: Partnership tab → Array Settings → *PARTNERSHIP* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `PARTNERSHIP_ENABLED` | a switch | Fallback tab | — | +| `PARTNERSHIP_OWNER_HOST` | a text box | in this section | "HOST1" or "HOST2" — flips on --transfer | +| `PARTNERSHIP_GRACE_HOURS` | a number box, in hours | in this section | hours after offboard before Tailscale removal | +| `PARTNERSHIP_OFFLINE_THRESHOLD` | a number box, in days | in this section | days either server unreachable before auto-offboard | +| `PARTNERSHIP_BLOCKLIST_FILE` | a text box | in this section | 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. | +| `VARAVERK_SETUP_FILE` | a text box | in this section | — | +| `PARTNERSHIP_REMOVE_TAILSCALE` | a switch | in this section | remove mirror from Tailscale tailnet on offboard | +| `TAILSCALE_API_KEY` | a masked box with a **Show** button | in this section | tskey-api-... | +| `TAILSCALE_TAILNET` | a text box | in this section | your tailnet name (e.g. yourname.github) | +| `PARTNERSHIP_TRANSFER_CONFIRM` | a text box | in this section | Transfer safety. | +| `PARTNERSHIP_TRANSFER_STRIKES` | a number box, in consecutive | in this section | consecutive health checks required | +| `PARTNERSHIP_TRANSFER_MAX_ATTEMPTS` | a number box | in this section | max health check attempts before giving up | +| `PARTNERSHIP_ONBOARD_VERIFY` | a switch | in this section | verify WebUI reachable after reconfiguration | +| `PARTNERSHIP_ONBOARD_NOTIFY` | a switch | in this section | notify both servers on completion | +| `PARTNERSHIP_SYNC_INTERVAL` | a number box, in minutes | in this section | minutes — informational, actual schedule in cron | +| `SSH_MAX_STRIKES` | a number box, in consecutive | in this section | consecutive SSH auth failures before critical notify | +| `SSH_STRIKE_RESET_HRS` | a number box, in hours | in this section | hours since last failure before strike counter resets | +| `PARTNERSHIP_FOLDERVIEW3` | a switch | in this section | create/remove FolderView3 folder on onboard/offboard | +| `PARTNERSHIP_FOLDERVIEW3_URL` | a text box | in this section | CA plugin URL — leave empty to skip auto-install | + +## PHP-FPM + +In `master.conf`. + +Route: Scheduler tab → **Php Fpm Max Children** → Config → *PHP-FPM* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `PHP_CONF` | a text box | in this section | Higher max_children allows more concurrent PHP requests to the unRAID WebGUI. Default is very low — increasing it prevents WebGUI slowdowns under load. 250 is safe for servers with 32GB+ RAM. | +| `PHP_MAX_CHILDREN` | a number box | in this section | — | + +## Play State Sync + +In `master.conf`. + +Route: Scheduler tab → **Play State Sync** → Config → *Play State Sync* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `PLAY_SYNC_ENABLED` | a switch | in this section | — | +| `PLAY_SYNC_REMOTE` | a switch | in this section | sync across hosts via Tailscale | +| `PLAY_SYNC_TYPES` | a text box | in this section | Audio excluded — music library too large; favorites handled separately | +| `PLAY_SYNC_FAV_TYPES` | a text box | in this section | union sync — never unmarks; Audio tracks future | +| `PLAY_SYNC_PROBE` | a switch | in this section | hash fetched state, skip per-item comparison when nothing changed | +| `PLAY_SYNC_PROBE_MAX_AGE_HOURS` | a number box | in this section | force a full comparison when the fingerprint is older than this | + +## Play State Sync — Handback + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Play State Sync** → Config → *Play State Sync — Handback* +- Scheduler tab → **Fallback** → Config → *Play State Sync — Handback* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `PLAY_SYNC_HANDBACK_RETRIES` | a number box, in attempts | in this section | attempts before giving up and proceeding to DNS cutover | +| `PLAY_SYNC_HANDBACK_RETRY_DELAY` | a number box, in seconds | in this section | seconds between retry attempts | + +## Pressure Thresholds + +In `master.conf`. + +Route: Scheduler tab → **Resource Watchdog** → Config → *Pressure Thresholds* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `RW_RAM_SOFT_GB` | a number box | in this section | throttle start — reduce background load | +| `RW_RAM_MEDIUM_GB` | a number box | in this section | pause background containers | +| `RW_RAM_HARD_GB` | a number box | in this section | stop optional containers (was SYS_WATCHDOG_MEM_SHUTDOWN_GB) | +| `RW_RAM_RECOVER_GB` | a number box | in this section | RAM must reach this before restoring hard-stopped containers | +| `RW_LOAD_SOFT_MULTIPLIER` | a text box | in this section | soft pressure: 2× cores sustained | +| `RW_LOAD_MEDIUM_MULTIPLIER` | a text box | in this section | medium pressure: 3× cores sustained | +| `RW_RECOVER_CYCLES` | a number box, in consecutive | in this section | Consecutive runs at lower pressure before de-escalating | + +## RAM Reboot Threshold + +In `master.conf`. + +Route: Scheduler tab → **Stability Watchdog** → Config → *RAM Reboot Threshold* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `SYS_WATCHDOG_MEM_GB` | a number box | in this section | strike system → reboot | + +## Reboot + +In `master.conf`. + +Route: Scheduler tab → **Server Reboot** → Config → *Reboot* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `REBOOT_SLEEP` | a number box, in seconds | in this section | Seconds of warning broadcast to logged-in users before server_reboot.sh reboots. Gives users time to save work — 300s = 5 minutes. | +| `REBOOT_VM_WAIT` | a number box, in seconds | in this section | Seconds to wait for graceful VM shutdown (ACPI signal via virsh) before libvirt stops it anyway — reboot takes priority. | + +## Rsync Defaults + +In `master.conf`. + +Route: Scheduler tab → **Rsync** → Config → *Rsync Defaults* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `BW_LIMIT` | a number box, in kb | Rsync tab | KB/s — 12500 ≈ 100Mbit | +| `RETRY_COUNT` | a number box | Rsync tab | retry attempts before giving up | +| `SLEEP` | a number box, in seconds | Rsync tab | seconds between retry attempts | +| `RSYNC_MAX_RUNTIME_HOURS` | a number box | in this section | cap per transfer attempt — pauses and resumes next scheduled run | +| `CRITICAL_CONTAINER_NAMES` | a list, one entry per line | in this section | — | +| `DELAYED_CONTAINERS` | a list, one entry per line | in this section | — | +| `CONTAINER_DELAY` | a number box, in seconds | in this section | seconds before starting delayed containers | +| `RESTART_VERIFY_WAIT` | a number box, in seconds | in this section | seconds to wait after restart before checking container is running | +| `EXCLUDE_DIRS` | a list, one entry per line | in this section | — | +| `DEFAULT_RSYNC_OPTS` | a list, one entry per line | in this section | --inplace writes directly to destination — delta against existing file, better for large media --partial keep partial file on interrupted transfer so next run resumes, not re-transfers --timeout kill stalled transfers instead of hanging indefinitely --numeric-ids use UIDs/GIDs numerically — prevents ownership mismatches between servers --delete intentionally omitted — the per-arr cleanups (lida… | + +## Rsync Enable/Disable + +In `master.conf`. + +Route: Scheduler tab → **Rsync** → Config → *Rsync Enable/Disable* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `RSYNC_ENABLED` | a switch | in this section | Tier 1 — global gate, overrides everything below | +| `CRITICAL_RSYNC_ENABLED` | a switch | in this section | Tier 2 — critical_sync_maintenance.sh rsync section | +| `INTERMEDIATE_RSYNC_ENABLED` | a switch | in this section | Tier 2 — intermediate_sync_maintenance.sh rsync section | +| `DAILY_RSYNC_ENABLED` | a switch | in this section | Tier 2 — daily_sync_maintenance.sh rsync section | +| `WEEKLY_RSYNC_ENABLED` | a switch | in this section | Tier 2 — weekly_sync_maintenance.sh rsync section | +| `MONTHLY_RSYNC_ENABLED` | a switch | in this section | Tier 2 — monthly_maintenance.sh rsync section | +| `FALLBACK_RSYNC_ENABLED` | a switch | Fallback tab | Tier 2 — fallback.sh writeback jobs on handback | + +## Rsync Merge Auto-Promote + +In `master.conf`. + +Route: Scheduler tab → **Rsync** → Config → *Rsync Merge Auto-Promote* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `RSYNC_MERGE_ENABLED` | a switch | in this section | When enabled, rsync.sh pre-scans the remote before syncing and promotes to merge mode (pull-then-push) if ≥75% of remote top-level entries exist locally. Named profiles are always excluded from auto-promote regardless of this toggle. | + +## Rsync Profile System + +In `master.conf`. + +Route: Scheduler tab → **Rsync** → Config → *Rsync Profile System* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `PROFILE_RSYNC_OPTS` | a list, one entry per line | in this section | — | +| `PROFILE_BW_LIMIT` | a list, one entry per line | in this section | Per-profile bandwidth limits in KB/s | +| `PROFILE_SLEEP` | a list, one entry per line | in this section | Seconds between retry attempts — only non-default profiles listed (default SLEEP applies otherwise) | +| `PROFILE_CRITICAL_CONTAINER_NAMES` | a list, one entry per line | in this section | Containers stopped on BOTH LOCAL and REMOTE before rsync. Local stops first — flushes databases cleanly. Remote stops next — prevents writes. Only running containers get restarted — stopped containers stay stopped. SPACE-SEPARATED STRINGS — converted to array at runtime | +| `PROFILE_DELAYED_CONTAINERS` | a list, one entry per line | in this section | Containers needing a delay after rsync before starting. SPACE-SEPARATED STRINGS — converted to array at runtime | +| `PROFILE_CONTAINER_DELAY` | a list, one entry per line | in this section | Seconds before starting delayed containers — only non-default profiles listed (default CONTAINER_DELAY applies otherwise) | +| `PROFILE_EXCLUDE_DIRS` | a list, one entry per line | in this section | Directories excluded from rsync per profile. SPACE-SEPARATED STRINGS — converted to array at runtime | +| `PROFILE_REMOTE_RESTART_CONTAINERS` | a list, one entry per line | in this section | Remote restart after dirty sync — restart these on remote IF they were running before sync. Same logic as stop/start — was stopped = stays stopped, was running = gets restarted. Used by dirty sync profiles (critical-fallback) so remote picks up changes. SPACE-SEPARATED STRINGS — converted to array at runtime | + +## SABnzbd Throttle + +In `master.conf`. + +Route: Scheduler tab → **Resource Watchdog** → Config → *SABnzbd Throttle* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `RW_SABNZBD_ENABLED` | a switch | in this section | Speed values: "50M" = 50 MB/s, "0" = unlimited | +| `RW_SABNZBD_SPEED_SOFT` | a text box | in this section | — | +| `RW_SABNZBD_SPEED_MEDIUM` | a text box | in this section | — | + +## SMART Health + +In `master.conf`. + +Route: Scheduler tab → **Smart Health** → Config → *SMART Health* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `SMART_TEMP_WARN` | a number box | in this section | fallback — Celsius warn threshold | +| `SMART_TEMP_CRIT` | a number box | in this section | fallback — Celsius critical threshold | + +## Strike and Reboot Loop Settings + +In `master.conf`. + +Route: Scheduler tab → **Stability Watchdog** → Config → *Strike and Reboot Loop Settings* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `SYS_WATCHDOG_STRIKE_LIMIT` | a number box, in consecutive | in this section | consecutive failures before reboot trigger | +| `SYS_WATCHDOG_REBOOT_LIMIT` | a number box | in this section | Reboot loop protection — if system keeps rebooting something is seriously wrong. After REBOOT_LIMIT reboots in REBOOT_WINDOW_HRS → shutdown instead of reboot. | +| `SYS_WATCHDOG_REBOOT_WINDOW_HRS` | a number box | in this section | — | + +## Sunday Morning Coffee Report + +In `master.conf`. + +Route: Scheduler tab → **Sunday Morning Coffee Report** → Config → *Sunday Morning Coffee Report* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `COFFEE_REPORT_SCRIPTS` | a list, one entry per line | in this section | Orchestrator that runs all Sunday monitor scripts in sequence. Schedule: 0 7 * * 0 (Sunday 7am — after weekly_sync_maintenance.sh finishes at ~3am) Each script runs independently and notifies on its own findings. | + +## Syslog Filter + +In `master.conf`. + +Route: Scheduler tab → **Docker Syslog Filter** → Config → *Syslog Filter* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `FILTER_FILE` | a text box | in this section | Path for the rsyslog filter file that suppresses Docker veth interface noise. Docker creates a new veth interface for each container — generates hundreds of log lines per hour that have no diagnostic value. Filter removes them at source. | + +## System Tuning Monitor + +In `master.conf`. + +Route: Scheduler tab → **System Tuning Monitor** → Config → *System Tuning Monitor* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `INOTIFY_WARN_PCT` | a number box | in this section | warn if inotify instances exceed this % of limit | +| `PHP_FPM_WARN_PCT` | a number box | in this section | warn if php-fpm workers exceed this % of max_children | +| `TUNING_MONITOR_LOG` | a text box | in this section | — | +| `TUNING_LOG_RETENTION` | a number box, in days | in this section | days before old entries are purged | + +## System Watchdog + +In `master.conf`. + +Route: Scheduler tab → **Watchdog Orchestrator** → Config → *System Watchdog* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `SYSTEM_WATCHDOG_SCRIPTS` | a list, one entry per line | in this section | system_watchdog.sh runs SYSTEM_WATCHDOG_SCRIPTS sequentially each cycle. Called by watchdog_orchestrator.sh — not scheduled directly. | + +## Transcode Management + +In `master.conf`. + +Route: Scheduler tab → **Transcode Management** → Config → *Transcode Management* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `TRANSCODE_MANAGEMENT_SCRIPTS` | a list, one entry per line | in this section | transcode_management.sh runs TRANSCODE_MANAGEMENT_SCRIPTS in order each cron cycle. Schedule: */7 * * * * (every 7 minutes) Order matters — cleanup first so the manager measures real current ramdisk usage, not usage inflated by stale segment files from ended sessions. | + +## Transcode Manager + +In `master.conf`. + +Reachable from: + +- Scheduler tab → **Transcode Management** → Config → *Transcode Manager* +- Scheduler tab → **Transcode Manager** → Config → *Transcode Manager* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `RAMDISK_PATH` | a text box | in this section | tmpfs mount point — created at array start by ramdisk_setup.sh. Must exist before Emby starts so the symlink resolves correctly. | +| `TRANSCODE_LINK` | a text box | in this section | Symlink that Emby points at — this path NEVER changes regardless of ramdisk/SSD state. Emby resolves the symlink once per session at start — symlink flips are transparent. Must match the container path configured in Emby's Extra Parameters. | +| `RAMDISK_SSD_MIN_GB` | a number box | in this section | Minimum free GB on SSD before allowing flip from ramdisk to SSD. | +| `TRANSCODE_MAX_AGE` | a number box, in minutes | in this section | minutes — HLS segment age before cleanup eligibility | +| `TRANSCODE_ORPHAN_AGE` | a number box, in minutes | in this section | minutes — files with no matching active session | +| `TRANSCODE_FLIP_WARN` | a number box | in this section | notify if symlink flips this many times in one hour | +| `TRANSCODE_OWNER` | a text box | in this section | — | +| `TRANSCODE_CHMOD` | a text box | in this section | — | +| `TRANSCODE_MANAGER_MODE` | a text box | in this section | Operating mode — controls symlink direction behaviour. smart — auto-flips between ramdisk and SSD based on thresholds (default) ramdisk — always uses ramdisk, warns if RAMDISK_WARN_GB exceeded but holds ssd — always uses SSD, never flips to ramdisk | +| `TRANSCODE_STATE_FILE` | a text box | in this section | Daily statistics log — read by weekly_health_digest.sh for transcode summary. | +| `TRANSCODE_DAILY_LOG` | a text box | in this section | — | +| `TRANSCODE_LOG_RETENTION` | a number box, in days | in this section | days before old entries purged | +| `TRANSCODE_CHECK_EMBY` | a switch | in this section | — | + +## Watchdog Orchestrator + +In `master.conf`. + +Route: Scheduler tab → **Watchdog Orchestrator** → Config → *Watchdog Orchestrator* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `WATCHDOG_ORCHESTRATOR_SCRIPTS` | a list, one entry per line | in this section | watchdog_orchestrator.sh runs WATCHDOG_ORCHESTRATOR_SCRIPTS in order each cron cycle. Schedule: */15 * * * * (every 15 minutes) NOT in ARRAY_START_SCRIPTS — has its own cron entry. Order matters — resource first (frees pressure), docker second (heals with freed resources), system third (storage + webgui component health), stability last (last line of defense). | + +## WebGUI Watchdog + +In `master.conf`. + +Route: Scheduler tab → **Webgui Watchdog** → Config → *WebGUI Watchdog* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `WEBGUI_URL` | a text box | in this section | Monitors unRAID WebGUI responsiveness — escalates through nginx restart → emhttp restart. Separate from docker_watchdog — this monitors the unRAID UI itself, not containers. | +| `WEBGUI_TIMEOUT` | a number box, in seconds | in this section | seconds before curl gives up on WebGUI check | +| `WEBGUI_NGINX_WAIT` | a number box, in seconds | in this section | seconds after nginx restart before rechecking | +| `WEBGUI_PHP_WAIT` | a number box, in seconds | in this section | seconds after php-fpm restart before rechecking | +| `WEBGUI_EMHTTP_WAIT` | a number box, in seconds | in this section | seconds after emhttp restart before rechecking | + +## Weekly Sync Maintenance + +In `master.conf`. + +Route: Scheduler tab → **Weekly Sync Maintenance** → Config → *Weekly Sync Maintenance* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `WEEKLY_MAINTENANCE_SCRIPTS` | a list, one entry per line | in this section | weekly_sync_maintenance.sh stops containers both sides → pulls updates → syncs WEEKLY_SYNC_SHARES → restarts → then iterates WEEKLY_MAINTENANCE_SCRIPTS. Schedule: 30 2 * * 0 (Sunday 2:30am) | +| `WEEKLY_CONTAINER_UPDATES` | a switch | in this section | Pull updates for WEEKLY_RESTART_CONTAINERS before docker_weekly_restart.sh runs. Set false to skip — docker_weekly_restart.sh still runs regardless. | +| `MONTHLY_REMAINING_UPDATES` | a switch | in this section | Pull updates for all running containers NOT in daily/weekly managed lists. Ensures every deployed container receives at least one image pull per month. Set false to skip. | +| `DOCKER_UPDATE_REBUILT_DAILY_FILE` | a text box | in this section | docker_update.sh rebuilds (stop+recreate onto new image) any container whose image changed, in every mode. For daily/weekly, that rebuild is immediately followed by the restart script's own unconditional pass — a container that just got rebuilt would be stopped and started again right after for no reason. docker_update.sh records which containers it rebuilt to these files; docker_daily_restart.… | +| `DOCKER_UPDATE_REBUILT_WEEKLY_FILE` | a text box | in this section | — | +| `DOCKER_UPDATE_REBUILT_STALE_HOURS` | a number box | in this section | — | +| `WEEKLY_SYNC_UPDATES` | a switch | in this section | pull container updates locally during weekly window | +| `WEEKLY_SYNC_UPDATES_REMOTE` | a switch | in this section | pull container updates on remote via SSH | + +## ZFS Memory Snapshot + +In `master.conf`. + +Route: Scheduler tab → **Zfs Memory Snapshot** → Config → *ZFS Memory Snapshot* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `ZFS_REPORT_LOG` | a text box | in this section | Weekly ZFS pool health and memory diagnostic report — informational only. | +| `ZFS_REPORT_ARC_WARN_PCT` | a number box | in this section | warn if ARC using more than this % of its max | +| `ZFS_REPORT_ARC_FREE_WARN_GB` | a number box | in this section | warn if ARC headroom (max - current) drops below this GB | +| `ZFS_REPORT_AVAIL_WARN_GB` | a number box | in this section | warn if less than this GB available on ZFS pool | +| `ZFS_REPORT_DOCKER_TOP` | a number box | in this section | how many top Docker containers to show by memory | + +## inotify Tuning + +In `master.conf`. + +Route: Scheduler tab → **Inotify Tuning** → Config → *inotify Tuning* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `INOTIFY_MAX_INSTANCES` | a number box | in this section | default: 128 — max inotify instances per user | +| `INOTIFY_MAX_WATCHES` | a number box | in this section | default: 8192 — SHARED budget across ALL users/containers | +| `INOTIFY_MAX_QUEUED_EVENTS` | a number box | in this section | default: 16384 — max events queued before dropping | + +## qBittorrent Throttle + +In `master.conf`. + +Route: Scheduler tab → **Resource Watchdog** → Config → *qBittorrent Throttle* + +| Setting | Control | Where | What it does | +|---|---|---|---| +| `RW_QBIT_ENABLED` | a switch | in this section | KB/s — 0 = unlimited | +| `RW_QBIT_DL_SOFT` | a number box | in this section | 50 MB/s | +| `RW_QBIT_DL_MEDIUM` | a number box | in this section | 10 MB/s | + +--- + +## Settings with no route through the UI + +These sections are not rendered by any page, so they can only be changed by editing +the conf file. If one of these is asked about, say so plainly rather than inventing a +route — mapping it into the Scheduler is a code change, not a setting. + +- **Authelia** (`host1.conf`) — `HOST1_AUTHELIA_CONFIG`, `HOST1_AUTHELIA_CONTAINER` +- **Fallback Tiers — What HOST1 Wants Covered When Down** (`host1.conf`) — `FALLBACK_HOST1_TIER1`, `FALLBACK_HOST1_TIER2`, `FALLBACK_HOST1_TIER3`, `FALLBACK_HOST1_TIER4` +- **Gitea** (`host1.conf`) — `HOST1_GITEA_API_TOKEN` +- **Identity** (`host1.conf`) — `HOST1_SSH_KEY`, `HOST1_STORAGE_PATH`, `HOST1_OWNER`, `HOST1_OWNER_EMAIL` +- **Jellyfin** (`host1.conf`) — `HOST1_JELLYFIN_CONTAINER`, `HOST1_JELLYFIN_URL`, `HOST1_JELLYFIN_API_KEY` +- **Monthly Sync Shares** (`host1.conf`) — `HOST1_MONTHLY_SYNC_SHARES` +- **NginxProxyManager** (`host1.conf`) — `HOST1_NPM_URL`, `HOST1_NPM_USER`, `HOST1_NPM_PASS` +- **PCIe AER Quiet** (`host1.conf`) — `HOST1_PCIE_QUIET_DEVICES` +- **Personal Shares** (`host1.conf`) — `HOST1_PERSONAL_SHARES` +- **RESOURCE MANAGER** (`host1.conf`) — `HOST1_RW_PAUSE_CONTAINERS`, `HOST1_RW_STOP_CONTAINERS` +- **Rsync Writeback** (`host1.conf`) — `HOST1_TIER1_WRITEBACK_DELAY`, `FALLBACK_HOST1_WRITEBACK_TIER1`, `FALLBACK_HOST1_WRITEBACK_TIER2`, `FALLBACK_HOST1_WRITEBACK_TIER3`, `FALLBACK_HOST1_WRITEBACK_TIER4` +- **TRANSCODES** (`host1.conf`) — `HOST1_RAMDISK_SIZE`, `HOST1_RAMDISK_WARN_GB`, `HOST1_RAMDISK_LOW_GB`, `HOST1_TRANSCODE_SSD`, `HOST1_TRANSCODE_SERVERS` +- **Tier Delays — HOST1 Outage Timers** (`host1.conf`) — `HOST1_TIER2_DELAY`, `HOST1_TIER3_DELAY`, `HOST1_TIER4_DELAY` +- **Unraid API** (`host1.conf`) — `HOST1_UNRAID_API_KEY` +- **Web search** (`host1.conf`) — `HOST1_DEGOOG_URL`, `HOST1_SEARXNG_URL`, `HOST1_WEB_SEARCH_API_KEY`, `HOST1_OLLAMA_EMBED_MODEL` +- **lldap** (`host1.conf`) — `HOST1_LLDAP_URL`, `HOST1_LLDAP_USER`, `HOST1_LLDAP_PASS` +- **Abort Toggles** (`master.conf`) — `SYS_WATCHDOG_ABORT_ON_ZFS_UNHEALTHY`, `SYS_WATCHDOG_ABORT_ON_PARITY`, `SYS_WATCHDOG_ABORT_ON_MOVER` +- **Arr Sync** (`master.conf`) — `ARR_SYNC_ENABLED`, `ARR_SYNC_BLOCKLIST`, `ARR_SYNC_CONNECT_TIMEOUT`, `ARR_SYNC_API_TIMEOUT`, `DOCKER_APPDATA_BASE`, `ARR_SYNC_LIDARR_PORT`, `ARR_SYNC_SONARR_PORT`, `ARR_SYNC_RADARR_PORT` +- **Cache Roots** (`master.conf`) — `VV_CACHE_ROOT`, `VV_CACHE_DIR`, `CONF_RAM_CACHE_DIR`, `ARR_CACHE_DIR`, `AI_TOKEN_CACHE_DIR`, `AI_JOB_DIR`, `DOCKER_JOB_DIR` +- **Conf Backups** (`master.conf`) — `CONF_BACKUP_RETAIN` +- **Conf Sync** (`master.conf`) — `CONF_SYNC_ENABLED` +- **Critical Containers** (`master.conf`) — `RW_CRITICAL_CONTAINERS` +- **Download Webhook** (`master.conf`) — `DOWNLOAD_WEBHOOK_ENABLED` +- **GIT / REPO** (`master.conf`) — `GITEA_CONTAINER`, `GITEA_REPO_PATH`, `GITEA_DOMAIN`, `TARGET_DIR`, `GITEA_SSH_KEY`, `SSH_PORT`, `GITEA_HTTP_PORT` +- **HOST IDENTITIES** (`master.conf`) — `HOST1`, `HOST2` +- **PCIe AER Quiet** (`master.conf`) — `PCIE_QUIET_ENABLED` +- **PER-HOST CONTAINER LISTS** (`master.conf`) — `RW_ENABLED`, `RW_STATE_FILE` +- **Remote Docker Daemon** (`master.conf`) — `REMOTE_DOCKER_STRIKE_LIMIT` +- **Remote Health Checks** (`master.conf`) — `ROOTFS_WARN` +- **SHARED HOST CONFIGURATION** (`master.conf`) — `DATA_DIR`, `DB_DIR`, `STATE_DIR`, `AI_DATA_DIR`, `CACHE_BACKUP_DIR`, `LOG_ARCHIVE_DIR`, `BACKUP_DIR`, `CONF_BACKUP_DIR`, `PERSISTENT_CONF_CACHE`, `ARR_CACHE_BACKUP_DIR` +- **State Files** (`master.conf`) — `SYS_WATCHDOG_STATE_FILE`, `DOCKER_WATCHDOG_FAILED_FILE`, `DOCKER_WATCHDOG_INTENTIONAL_FILE`, `SYS_WATCHDOG_REBOOT_LOG`, `SYS_WATCHDOG_OOM_FILE` +- **Thresholds** (`master.conf`) — `SYS_WATCHDOG_ROOTFS_PCT`, `SYS_WATCHDOG_ROOTFS_CRITICAL_PCT`, `SYS_WATCHDOG_LOG_PCT`, `SYS_WATCHDOG_TMP_PCT`, `SYS_WATCHDOG_TMP_CRITICAL_PCT`, `SYS_WATCHDOG_ARC_PINNED_PCT`, `SYS_WATCHDOG_ARC_RELEASE_PCT`, `SYS_WATCHDOG_LOAD_MULTIPLIER`, `SYS_WATCHDOG_ZOMBIE_LIMIT`, `SYS_WATCHDOG_CPU_TEMP_MAX`, `SYS_WATCHDOG_FD_CRITICAL_PCT`, `SYS_WATCHDOG_RUNAWAY_CPU_PCT`, `SYS_WATCHDOG_RUNAWAY_STRIKES`, `SYS_WATCHDOG_MDSTAT_ERROR_LIMIT` +- **Version Parity** (`master.conf`) — `UNRAID_VERSION_MISMATCH_ACTION`