{"type":"mode","mode":"normal","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"permission-mode","permissionMode":"default","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"file-history-snapshot","messageId":"6f492114-db7f-44d5-a361-cef48804bf85","snapshot":{"messageId":"6f492114-db7f-44d5-a361-cef48804bf85","trackedFileBackups":{},"timestamp":"2026-06-06T17:48:42.280Z"},"isSnapshotUpdate":false} {"parentUuid":null,"isSidechain":false,"promptId":"0a108bc8-cb46-4a98-91cc-27f1cab8a949","type":"user","message":{"role":"user","content":"NPM auth failed — check credentials in host conf"},"uuid":"6f492114-db7f-44d5-a361-cef48804bf85","timestamp":"2026-06-06T17:48:42.280Z","permissionMode":"default","promptSource":"typed","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"6f492114-db7f-44d5-a361-cef48804bf85","isSidechain":false,"attachment":{"type":"deferred_tools_delta","addedNames":["CronCreate","CronDelete","CronList","EnterPlanMode","EnterWorktree","ExitPlanMode","ExitWorktree","Monitor","NotebookEdit","PushNotification","RemoteTrigger","TaskCreate","TaskGet","TaskList","TaskOutput","TaskStop","TaskUpdate","WebFetch","WebSearch","mcp__claude_ai_Google_Drive__authenticate","mcp__claude_ai_Google_Drive__complete_authentication"],"addedLines":["CronCreate","CronDelete","CronList","EnterPlanMode","EnterWorktree","ExitPlanMode","ExitWorktree","Monitor","NotebookEdit","PushNotification","RemoteTrigger","TaskCreate","TaskGet","TaskList","TaskOutput","TaskStop","TaskUpdate","WebFetch","WebSearch","mcp__claude_ai_Google_Drive__authenticate","mcp__claude_ai_Google_Drive__complete_authentication"],"removedNames":[],"readdedNames":[],"pendingMcpServers":[]},"type":"attachment","uuid":"2e283ef8-35f1-4e68-b9cc-8bfce7c2a826","timestamp":"2026-06-06T17:48:42.279Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"2e283ef8-35f1-4e68-b9cc-8bfce7c2a826","isSidechain":false,"attachment":{"type":"skill_listing","content":"- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.\n- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: \"rebind ctrl+s\", \"add a chord shortcut\", \"change the submit key\", \"customize keybindings\".\n- verify: Verify that a code change actually does what it's supposed to by running the app and observing behavior. Use when asked to verify a PR, confirm a fix works, test a change manually, check that a feature works, or validate local changes before pushing.\n- code-review: Review the current diff for correctness bugs and reuse/simplification/efficiency cleanups at the given effort level (low/medium: fewer, high-confidence findings; high→max: broader coverage, may include uncertain findings; ultra: deep multi-agent review in the cloud). Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review.\n- simplify: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.\n- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.\n- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. \"check the deploy every 5 minutes\", \"keep running /babysit-prs\"). Do NOT invoke for one-off tasks.\n- schedule: Create, update, list, or run scheduled remote agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring remote agent, set up automated tasks, create a cron job for Claude Code, or manage their scheduled agents/routines. Also use when the user wants a one-time scheduled run (\"run this once at 3pm\", \"remind me to check X tomorrow\").\n- claude-api: Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).\n- run: Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).\n- init: Initialize a new CLAUDE.md file with codebase documentation\n- review: Review a pull request\n- security-review: Complete a security review of the pending changes on the current branch","skillCount":13,"isInitial":true,"names":["update-config","keybindings-help","verify","code-review","simplify","fewer-permission-prompts","loop","schedule","claude-api","run","init","review","security-review"]},"type":"attachment","uuid":"2703fbae-a97f-4132-94cf-6c9e9fca70d8","timestamp":"2026-06-06T17:48:42.279Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"ai-title","aiTitle":"Troubleshoot NPM authentication failure","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"parentUuid":"2703fbae-a97f-4132-94cf-6c9e9fca70d8","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_011ZhgKqm6m3AYZhQJDRzdd4","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"Eu8CCmUIDhgCKkBBLJ2qJsOCvBQMua/vq4tsfT69DGK1HaWzl5B3O0x5QR7z6FSdX1WFInctsR1tuBBneZlDLg5xABWGUdjD3QGPMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIM07JAeOV62aiR6sdCGgzFW5PcO18x+++FW4AiMG4qvfDBX1Q7wYgdUQXyKheCljbe+mXseeuYcWDZLkGGtZnlGrYzBWQf33F2FOe2Uyq3AZMj/Uq5Q6/BNtphVa601f/URlNLzsW6gcmLYO2GK7ziTr33sEKJok1sVpiu/h1WOLHGSF2BdnXAr/NS3bHrwUjKX+heo3e8i87LH/SJ8C9b1Aa7sTi4kfRgBAoQF8x2XCS/9G02lmy3oM6nMfKJKtK9B81qAH7IZdwxPDFPfYu5R21MQ4GBo3BNaFCo40SU3APkRFhjpq5yJjWG0dtO9ZkkMdIJ4xjVTiDKvSaWDWgmCzksboWLWhgB"}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":6211,"cache_read_input_tokens":12030,"output_tokens":155,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":6211,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":155,"cache_read_input_tokens":12030,"cache_creation_input_tokens":6211,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":6211},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRjjCPfK9ecwNR8PDNb","type":"assistant","uuid":"41f58e9c-66b3-4b74-bade-47b834b08f7d","timestamp":"2026-06-06T17:48:44.936Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"41f58e9c-66b3-4b74-bade-47b834b08f7d","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_011ZhgKqm6m3AYZhQJDRzdd4","type":"message","role":"assistant","content":[{"type":"text","text":"Let me check the NPM configuration in the plugin directory."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":6211,"cache_read_input_tokens":12030,"output_tokens":155,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":6211,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":155,"cache_read_input_tokens":12030,"cache_creation_input_tokens":6211,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":6211},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRjjCPfK9ecwNR8PDNb","type":"assistant","uuid":"3a8d322c-aedf-42ef-8311-5ac85bf5e02d","timestamp":"2026-06-06T17:48:45.481Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"3a8d322c-aedf-42ef-8311-5ac85bf5e02d","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_011ZhgKqm6m3AYZhQJDRzdd4","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_016TQGAqvLus153rrmbnaoo8","name":"Bash","input":{"command":"find /boot/config/plugins/varaverk -type f | sort","description":"List all files in varaverk plugin"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":6211,"cache_read_input_tokens":12030,"output_tokens":155,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":6211,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":155,"cache_read_input_tokens":12030,"cache_creation_input_tokens":6211,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":6211},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRjjCPfK9ecwNR8PDNb","type":"assistant","uuid":"30715d8a-0eec-437d-b7a8-5815668cec22","timestamp":"2026-06-06T17:48:46.139Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"30715d8a-0eec-437d-b7a8-5815668cec22","isSidechain":false,"promptId":"0a108bc8-cb46-4a98-91cc-27f1cab8a949","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_016TQGAqvLus153rrmbnaoo8","type":"tool_result","content":"/boot/config/plugins/varaverk/.claude/settings.json\n/boot/config/plugins/varaverk/.git/COMMIT_EDITMSG\n/boot/config/plugins/varaverk/.git/FETCH_HEAD\n/boot/config/plugins/varaverk/.git/HEAD\n/boot/config/plugins/varaverk/.git/ORIG_HEAD\n/boot/config/plugins/varaverk/.git/config\n/boot/config/plugins/varaverk/.git/description\n/boot/config/plugins/varaverk/.git/hooks/applypatch-msg.sample\n/boot/config/plugins/varaverk/.git/hooks/commit-msg.sample\n/boot/config/plugins/varaverk/.git/hooks/fsmonitor-watchman.sample\n/boot/config/plugins/varaverk/.git/hooks/post-update.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-applypatch.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-commit.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-merge-commit.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-push.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-rebase.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-receive.sample\n/boot/config/plugins/varaverk/.git/hooks/prepare-commit-msg.sample\n/boot/config/plugins/varaverk/.git/hooks/push-to-checkout.sample\n/boot/config/plugins/varaverk/.git/hooks/sendemail-validate.sample\n/boot/config/plugins/varaverk/.git/hooks/update.sample\n/boot/config/plugins/varaverk/.git/index\n/boot/config/plugins/varaverk/.git/info/exclude\n/boot/config/plugins/varaverk/.git/info/refs\n/boot/config/plugins/varaverk/.git/info/sparse-checkout\n/boot/config/plugins/varaverk/.git/logs/HEAD\n/boot/config/plugins/varaverk/.git/logs/refs/heads/main\n/boot/config/plugins/varaverk/.git/logs/refs/remotes/origin/HEAD\n/boot/config/plugins/varaverk/.git/logs/refs/remotes/origin/main\n/boot/config/plugins/varaverk/.git/objects/info/packs\n/boot/config/plugins/varaverk/.git/objects/pack/multi-pack-index\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.idx\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.mtimes\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.pack\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.rev\n/boot/config/plugins/varaverk/.git/objects/pack/pack-99722f27ede78c647526dad27cadaf67e71789ce.idx\n/boot/config/plugins/varaverk/.git/objects/pack/pack-99722f27ede78c647526dad27cadaf67e71789ce.pack\n/boot/config/plugins/varaverk/.git/objects/pack/pack-99722f27ede78c647526dad27cadaf67e71789ce.rev\n/boot/config/plugins/varaverk/.git/objects/pack/pack-aec4022dc06fae8f456da604eee264e0bb54f8b9.idx\n/boot/config/plugins/varaverk/.git/objects/pack/pack-aec4022dc06fae8f456da604eee264e0bb54f8b9.pack\n/boot/config/plugins/varaverk/.git/objects/pack/pack-aec4022dc06fae8f456da604eee264e0bb54f8b9.rev\n/boot/config/plugins/varaverk/.git/refs/heads/main\n/boot/config/plugins/varaverk/.git/refs/remotes/origin/HEAD\n/boot/config/plugins/varaverk/.git/refs/remotes/origin/main\n/boot/config/plugins/varaverk/.git/shallow\n/boot/config/plugins/varaverk/.gitignore\n/boot/config/plugins/varaverk/.vscode/settings.json\n/boot/config/plugins/varaverk/Configurations/host.conf.template\n/boot/config/plugins/varaverk/Configurations/host1.conf\n/boot/config/plugins/varaverk/Configurations/host1.conf.bak\n/boot/config/plugins/varaverk/Configurations/host2.conf\n/boot/config/plugins/varaverk/Configurations/master.conf\n/boot/config/plugins/varaverk/Configurations/master.conf.bak\n/boot/config/plugins/varaverk/Configurations/master.conf.template\n/boot/config/plugins/varaverk/Deployment/conf_templates/host.conf.template\n/boot/config/plugins/varaverk/Deployment/conf_templates/master.conf\n/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh\n/boot/config/plugins/varaverk/Deployment/deploy.sh\n/boot/config/plugins/varaverk/Docker_Essentials/Manual-Docker_Essentials.md\n/boot/config/plugins/varaverk/Docker_Essentials/README-Docker_Essentials.md\n/boot/config/plugins/varaverk/Docker_Essentials/docker_container_stop.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_daily_restart.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_network_connect.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_update.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_update_remaining.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_weekly_restart.sh\n/boot/config/plugins/varaverk/Docker_Essentials/downloaders_reset.sh\n/boot/config/plugins/varaverk/Fallback/Manual-Fallback.md\n/boot/config/plugins/varaverk/Fallback/README-Fallback.md\n/boot/config/plugins/varaverk/Fallback/fallback.sh\n/boot/config/plugins/varaverk/Fallback/fallback_test.sh\n/boot/config/plugins/varaverk/Kernel/README-Kernel.md\n/boot/config/plugins/varaverk/Kernel/decision_engine.sh\n/boot/config/plugins/varaverk/Manual.md\n/boot/config/plugins/varaverk/Media/Manual-Media.md\n/boot/config/plugins/varaverk/Media/README-Media.md\n/boot/config/plugins/varaverk/Media/arr_sync.sh\n/boot/config/plugins/varaverk/Media/arrs_failed_stalled_recovery.sh\n/boot/config/plugins/varaverk/Media/lidarr_cleanup.sh\n/boot/config/plugins/varaverk/Media/lidarr_missing_art.sh\n/boot/config/plugins/varaverk/Media/media_cleaner.sh\n/boot/config/plugins/varaverk/Media/media_shares_permissions.sh\n/boot/config/plugins/varaverk/Media/play_state_sync.sh\n/boot/config/plugins/varaverk/Media/playback_aware_lidarr_discovery.sh\n/boot/config/plugins/varaverk/Media/playback_aware_radarr_discovery.sh\n/boot/config/plugins/varaverk/Media/playback_aware_sonarr_discovery.sh\n/boot/config/plugins/varaverk/Media/radarr_cleanup.sh\n/boot/config/plugins/varaverk/Media/radarr_tmdb_removed.sh\n/boot/config/plugins/varaverk/Media/sonarr_cleanup.sh\n/boot/config/plugins/varaverk/Media/sonarr_tvdb_removed.sh\n/boot/config/plugins/varaverk/Monitors/Manual-Monitors.md\n/boot/config/plugins/varaverk/Monitors/README-Monitors.md\n/boot/config/plugins/varaverk/Monitors/backup_verify.sh\n/boot/config/plugins/varaverk/Monitors/bandwidth_monitor.sh\n/boot/config/plugins/varaverk/Monitors/cert_monitor.sh\n/boot/config/plugins/varaverk/Monitors/emby_session_report.sh\n/boot/config/plugins/varaverk/Monitors/mesh_monitor.sh\n/boot/config/plugins/varaverk/Monitors/smart_health.sh\n/boot/config/plugins/varaverk/Monitors/system_tuning_monitor.sh\n/boot/config/plugins/varaverk/Monitors/weekly_health_digest.sh\n/boot/config/plugins/varaverk/Monitors/zfs_memory_snapshot.sh\n/boot/config/plugins/varaverk/Notes_To-Do.md\n/boot/config/plugins/varaverk/Old_Arch_Still_Works/arr_cleanup.py\n/boot/config/plugins/varaverk/Old_Arch_Still_Works/arr_cleanup.sh\n/boot/config/plugins/varaverk/Old_Arch_Still_Works/continuous_scripts_status.sh\n/boot/config/plugins/varaverk/Orchestrators/README-Orchestrators.md\n/boot/config/plugins/varaverk/Orchestrators/array_started.sh\n/boot/config/plugins/varaverk/Orchestrators/array_stopping.sh\n/boot/config/plugins/varaverk/Orchestrators/critical_sync_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/daily_sync_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/intermediate_sync_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/monthly_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/sunday_morning_coffee_report.sh\n/boot/config/plugins/varaverk/Orchestrators/transcode_management.sh\n/boot/config/plugins/varaverk/Orchestrators/watchdog_orchestrator.sh\n/boot/config/plugins/varaverk/Orchestrators/weekly_sync_maintenance.sh\n/boot/config/plugins/varaverk/Partnership/Manual-Partnership.md\n/boot/config/plugins/varaverk/Partnership/README-Partnership.md\n/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh\n/boot/config/plugins/varaverk/Partnership/onboard_cancel.sh\n/boot/config/plugins/varaverk/Partnership/partnership_manager.sh\n/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh\n/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh\n/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh\n/boot/config/plugins/varaverk/Partnership/ssh_setup.sh\n/boot/config/plugins/varaverk/Plugin/Icons/Varaverk.png\n/boot/config/plugins/varaverk/Plugin/Manual-Plugin.md\n/boot/config/plugins/varaverk/Plugin/README-Plugin.md\n/boot/config/plugins/varaverk/Plugin/build.sh\n/boot/config/plugins/varaverk/Plugin/dev_install.sh\n/boot/config/plugins/varaverk/Plugin/dist/varaverk-2026.05.31-noarch-1.txz.sha256\n/boot/config/plugins/varaverk/Plugin/unraid/Partnership/containers.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/mover_stop.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/php_fpm_max_children.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/user_scripts_stop.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/conf_populate.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/recreate_shares.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Varaverk.page\n/boot/config/plugins/varaverk/Plugin/unraid/VaraverkSettings.page\n/boot/config/plugins/varaverk/Plugin/unraid/Watchdogs/System/webgui_watchdog.sh\n/boot/config/plugins/varaverk/Plugin/unraid/adapter.sh\n/boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/arrs.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/auth.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/board.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/cert.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/checklist.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/clearlock.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/conf_toggle.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/confform.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/config.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/create_api_key.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/docker.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/docker_action.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/dryrun.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/fallback.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/flag_toggle.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/log.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/media.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/monitor.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/monitor_fast.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/monitor_remote.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/movescript.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/partnership.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/partnership_ping.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/partnership_settings.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rawconf.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/readscript.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/recent.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/reorderarray.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_profiles.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_standalone.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_win_arrays.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/run.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/savefolders.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/scheduler.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/script.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/scriptinfo.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/settings.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/snapshot.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/status.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/stop.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/system.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/watchdog.php\n/boot/config/plugins/varaverk/Plugin/unraid/css/varaverk.css\n/boot/config/plugins/varaverk/Plugin/unraid/event/disks_mounted/array_start_jobs\n/boot/config/plugins/varaverk/Plugin/unraid/event/disks_mounted/rebuild_cron\n/boot/config/plugins/varaverk/Plugin/unraid/event/disks_unmounting/array_stop_jobs\n/boot/config/plugins/varaverk/Plugin/unraid/icons/varaverk.png\n/boot/config/plugins/varaverk/Plugin/unraid/include/arrs.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/common.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/confform.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/config.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/docker.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/docker_folders.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/docs.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/fallback.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/media.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/monitor.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/vms.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/watchdog.php\n/boot/config/plugins/varaverk/Plugin/unraid/js/varaverk.js\n/boot/config/plugins/varaverk/Plugin/unraid/pages/arrs.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/auth.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/docker.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/fallback.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/monitor.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/rsync.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/scheduler.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/settings.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/setup.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/watchdog.php\n/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh\n/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh\n/boot/config/plugins/varaverk/Plugin/varaverk.plg\n/boot/config/plugins/varaverk/README-User_Script_Plug-in.md\n/boot/config/plugins/varaverk/README.md\n/boot/config/plugins/varaverk/Rsync/Manual-Rsync.md\n/boot/config/plugins/varaverk/Rsync/README-Rsync.md\n/boot/config/plugins/varaverk/Rsync/rsync.sh\n/boot/config/plugins/varaverk/State_Files/container_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/docker_watchdog_failed.db\n/boot/config/plugins/varaverk/State_Files/docker_watchdog_manual_stop.db\n/boot/config/plugins/varaverk/State_Files/fallback_state.db\n/boot/config/plugins/varaverk/State_Files/network_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/partnership_offline_days.db\n/boot/config/plugins/varaverk/State_Files/resource_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/storage_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/system_watchdog_oom.db\n/boot/config/plugins/varaverk/State_Files/system_watchdog_reboots.db\n/boot/config/plugins/varaverk/State_Files/system_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/transcode_state.db\n/boot/config/plugins/varaverk/State_Files/varaverk_setup.db\n/boot/config/plugins/varaverk/State_Files/watchdog_appdata_growth.db\n/boot/config/plugins/varaverk/State_Files/watchdog_orch_hb.count\n/boot/config/plugins/varaverk/State_Files/watchdog_orchestrator.log\n/boot/config/plugins/varaverk/System_Essentials/Manual-System_Essentials.md\n/boot/config/plugins/varaverk/System_Essentials/README-System_Essentials.md\n/boot/config/plugins/varaverk/System_Essentials/clear_logs.sh\n/boot/config/plugins/varaverk/System_Essentials/conf_sync.sh\n/boot/config/plugins/varaverk/System_Essentials/docker_syslog_filter.sh\n/boot/config/plugins/varaverk/System_Essentials/inotify_tuning.sh\n/boot/config/plugins/varaverk/System_Essentials/rsync_stop.sh\n/boot/config/plugins/varaverk/System_Essentials/server_reboot.sh\n/boot/config/plugins/varaverk/Tools/Manual-Tools.md\n/boot/config/plugins/varaverk/Tools/README-Tools.md\n/boot/config/plugins/varaverk/Tools/bulk_permissions_repair.sh\n/boot/config/plugins/varaverk/Tools/claude_startup.sh\n/boot/config/plugins/varaverk/Tools/container_data_export.sh\n/boot/config/plugins/varaverk/Tools/docker_prune_images.sh\n/boot/config/plugins/varaverk/Tools/emby_database_repair.sh\n/boot/config/plugins/varaverk/Tools/emby_to_lidarr_sync.sh\n/boot/config/plugins/varaverk/Tools/emby_to_radarr_sync.sh\n/boot/config/plugins/varaverk/Tools/emby_to_sonarr_sync.sh\n/boot/config/plugins/varaverk/Tools/fallback_state_reset.sh\n/boot/config/plugins/varaverk/Tools/ramdisk_stop.sh\n/boot/config/plugins/varaverk/Tools/smart_long_test.sh\n/boot/config/plugins/varaverk/Tools/watchdog_skip_list_manager.sh\n/boot/config/plugins/varaverk/Tools/zfs_pool_scrub.sh\n/boot/config/plugins/varaverk/Transcodes/Manual-Transcoding.md\n/boot/config/plugins/varaverk/Transcodes/README-Transcoding.md\n/boot/config/plugins/varaverk/Transcodes/ramdisk_setup.sh\n/boot/config/plugins/varaverk/Transcodes/transcode_cleanup.sh\n/boot/config/plugins/varaverk/Transcodes/transcode_manager.sh\n/boot/config/plugins/varaverk/Watchdogs/Manual-Watchdogs.md\n/boot/config/plugins/varaverk/Watchdogs/README-Watchdogs.md\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/System/storage_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/docker_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/resource_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/stability_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/system_watchdog.sh\n/boot/config/plugins/varaverk/common.sh\n/boot/config/plugins/varaverk/data/arr_cleanup_stats.db\n/boot/config/plugins/varaverk/data/arr_recovery_stats.db\n/boot/config/plugins/varaverk/data/bandwidth_history.db\n/boot/config/plugins/varaverk/data/container_restart_history.db\n/boot/config/plugins/varaverk/data/lidarr_discovery_history.db\n/boot/config/plugins/varaverk/data/lidarr_tracked.count\n/boot/config/plugins/varaverk/data/radarr_discovery_history.db\n/boot/config/plugins/varaverk/data/sonarr_discovery_history.db\n/boot/config/plugins/varaverk/data/transcode_daily.db\n/boot/config/plugins/varaverk/docker_folders.json\n/boot/config/plugins/varaverk/git_pull_execute.sh\n/boot/config/plugins/varaverk/install.log\n/boot/config/plugins/varaverk/load_config.sh\n/boot/config/plugins/varaverk/schedule.json\n/boot/config/plugins/varaverk/varaverk-2026.05.31-noarch-1.txz\n/boot/config/plugins/varaverk/varaverk.cfg\n/boot/config/plugins/varaverk/varaverk.cron","is_error":false}]},"uuid":"7a44cd18-ae03-4c1a-891c-37daa0201ef7","timestamp":"2026-06-06T17:48:50.082Z","toolUseResult":{"stdout":"/boot/config/plugins/varaverk/.claude/settings.json\n/boot/config/plugins/varaverk/.git/COMMIT_EDITMSG\n/boot/config/plugins/varaverk/.git/FETCH_HEAD\n/boot/config/plugins/varaverk/.git/HEAD\n/boot/config/plugins/varaverk/.git/ORIG_HEAD\n/boot/config/plugins/varaverk/.git/config\n/boot/config/plugins/varaverk/.git/description\n/boot/config/plugins/varaverk/.git/hooks/applypatch-msg.sample\n/boot/config/plugins/varaverk/.git/hooks/commit-msg.sample\n/boot/config/plugins/varaverk/.git/hooks/fsmonitor-watchman.sample\n/boot/config/plugins/varaverk/.git/hooks/post-update.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-applypatch.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-commit.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-merge-commit.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-push.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-rebase.sample\n/boot/config/plugins/varaverk/.git/hooks/pre-receive.sample\n/boot/config/plugins/varaverk/.git/hooks/prepare-commit-msg.sample\n/boot/config/plugins/varaverk/.git/hooks/push-to-checkout.sample\n/boot/config/plugins/varaverk/.git/hooks/sendemail-validate.sample\n/boot/config/plugins/varaverk/.git/hooks/update.sample\n/boot/config/plugins/varaverk/.git/index\n/boot/config/plugins/varaverk/.git/info/exclude\n/boot/config/plugins/varaverk/.git/info/refs\n/boot/config/plugins/varaverk/.git/info/sparse-checkout\n/boot/config/plugins/varaverk/.git/logs/HEAD\n/boot/config/plugins/varaverk/.git/logs/refs/heads/main\n/boot/config/plugins/varaverk/.git/logs/refs/remotes/origin/HEAD\n/boot/config/plugins/varaverk/.git/logs/refs/remotes/origin/main\n/boot/config/plugins/varaverk/.git/objects/info/packs\n/boot/config/plugins/varaverk/.git/objects/pack/multi-pack-index\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.idx\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.mtimes\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.pack\n/boot/config/plugins/varaverk/.git/objects/pack/pack-214c8e0f32bdb4d0c64ae508363889f0bc3aa8a8.rev\n/boot/config/plugins/varaverk/.git/objects/pack/pack-99722f27ede78c647526dad27cadaf67e71789ce.idx\n/boot/config/plugins/varaverk/.git/objects/pack/pack-99722f27ede78c647526dad27cadaf67e71789ce.pack\n/boot/config/plugins/varaverk/.git/objects/pack/pack-99722f27ede78c647526dad27cadaf67e71789ce.rev\n/boot/config/plugins/varaverk/.git/objects/pack/pack-aec4022dc06fae8f456da604eee264e0bb54f8b9.idx\n/boot/config/plugins/varaverk/.git/objects/pack/pack-aec4022dc06fae8f456da604eee264e0bb54f8b9.pack\n/boot/config/plugins/varaverk/.git/objects/pack/pack-aec4022dc06fae8f456da604eee264e0bb54f8b9.rev\n/boot/config/plugins/varaverk/.git/refs/heads/main\n/boot/config/plugins/varaverk/.git/refs/remotes/origin/HEAD\n/boot/config/plugins/varaverk/.git/refs/remotes/origin/main\n/boot/config/plugins/varaverk/.git/shallow\n/boot/config/plugins/varaverk/.gitignore\n/boot/config/plugins/varaverk/.vscode/settings.json\n/boot/config/plugins/varaverk/Configurations/host.conf.template\n/boot/config/plugins/varaverk/Configurations/host1.conf\n/boot/config/plugins/varaverk/Configurations/host1.conf.bak\n/boot/config/plugins/varaverk/Configurations/host2.conf\n/boot/config/plugins/varaverk/Configurations/master.conf\n/boot/config/plugins/varaverk/Configurations/master.conf.bak\n/boot/config/plugins/varaverk/Configurations/master.conf.template\n/boot/config/plugins/varaverk/Deployment/conf_templates/host.conf.template\n/boot/config/plugins/varaverk/Deployment/conf_templates/master.conf\n/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh\n/boot/config/plugins/varaverk/Deployment/deploy.sh\n/boot/config/plugins/varaverk/Docker_Essentials/Manual-Docker_Essentials.md\n/boot/config/plugins/varaverk/Docker_Essentials/README-Docker_Essentials.md\n/boot/config/plugins/varaverk/Docker_Essentials/docker_container_stop.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_daily_restart.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_network_connect.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_update.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_update_remaining.sh\n/boot/config/plugins/varaverk/Docker_Essentials/docker_weekly_restart.sh\n/boot/config/plugins/varaverk/Docker_Essentials/downloaders_reset.sh\n/boot/config/plugins/varaverk/Fallback/Manual-Fallback.md\n/boot/config/plugins/varaverk/Fallback/README-Fallback.md\n/boot/config/plugins/varaverk/Fallback/fallback.sh\n/boot/config/plugins/varaverk/Fallback/fallback_test.sh\n/boot/config/plugins/varaverk/Kernel/README-Kernel.md\n/boot/config/plugins/varaverk/Kernel/decision_engine.sh\n/boot/config/plugins/varaverk/Manual.md\n/boot/config/plugins/varaverk/Media/Manual-Media.md\n/boot/config/plugins/varaverk/Media/README-Media.md\n/boot/config/plugins/varaverk/Media/arr_sync.sh\n/boot/config/plugins/varaverk/Media/arrs_failed_stalled_recovery.sh\n/boot/config/plugins/varaverk/Media/lidarr_cleanup.sh\n/boot/config/plugins/varaverk/Media/lidarr_missing_art.sh\n/boot/config/plugins/varaverk/Media/media_cleaner.sh\n/boot/config/plugins/varaverk/Media/media_shares_permissions.sh\n/boot/config/plugins/varaverk/Media/play_state_sync.sh\n/boot/config/plugins/varaverk/Media/playback_aware_lidarr_discovery.sh\n/boot/config/plugins/varaverk/Media/playback_aware_radarr_discovery.sh\n/boot/config/plugins/varaverk/Media/playback_aware_sonarr_discovery.sh\n/boot/config/plugins/varaverk/Media/radarr_cleanup.sh\n/boot/config/plugins/varaverk/Media/radarr_tmdb_removed.sh\n/boot/config/plugins/varaverk/Media/sonarr_cleanup.sh\n/boot/config/plugins/varaverk/Media/sonarr_tvdb_removed.sh\n/boot/config/plugins/varaverk/Monitors/Manual-Monitors.md\n/boot/config/plugins/varaverk/Monitors/README-Monitors.md\n/boot/config/plugins/varaverk/Monitors/backup_verify.sh\n/boot/config/plugins/varaverk/Monitors/bandwidth_monitor.sh\n/boot/config/plugins/varaverk/Monitors/cert_monitor.sh\n/boot/config/plugins/varaverk/Monitors/emby_session_report.sh\n/boot/config/plugins/varaverk/Monitors/mesh_monitor.sh\n/boot/config/plugins/varaverk/Monitors/smart_health.sh\n/boot/config/plugins/varaverk/Monitors/system_tuning_monitor.sh\n/boot/config/plugins/varaverk/Monitors/weekly_health_digest.sh\n/boot/config/plugins/varaverk/Monitors/zfs_memory_snapshot.sh\n/boot/config/plugins/varaverk/Notes_To-Do.md\n/boot/config/plugins/varaverk/Old_Arch_Still_Works/arr_cleanup.py\n/boot/config/plugins/varaverk/Old_Arch_Still_Works/arr_cleanup.sh\n/boot/config/plugins/varaverk/Old_Arch_Still_Works/continuous_scripts_status.sh\n/boot/config/plugins/varaverk/Orchestrators/README-Orchestrators.md\n/boot/config/plugins/varaverk/Orchestrators/array_started.sh\n/boot/config/plugins/varaverk/Orchestrators/array_stopping.sh\n/boot/config/plugins/varaverk/Orchestrators/critical_sync_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/daily_sync_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/intermediate_sync_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/monthly_maintenance.sh\n/boot/config/plugins/varaverk/Orchestrators/sunday_morning_coffee_report.sh\n/boot/config/plugins/varaverk/Orchestrators/transcode_management.sh\n/boot/config/plugins/varaverk/Orchestrators/watchdog_orchestrator.sh\n/boot/config/plugins/varaverk/Orchestrators/weekly_sync_maintenance.sh\n/boot/config/plugins/varaverk/Partnership/Manual-Partnership.md\n/boot/config/plugins/varaverk/Partnership/README-Partnership.md\n/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh\n/boot/config/plugins/varaverk/Partnership/onboard_cancel.sh\n/boot/config/plugins/varaverk/Partnership/partnership_manager.sh\n/boot/config/plugins/varaverk/Partnership/partnership_offboard.sh\n/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh\n/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh\n/boot/config/plugins/varaverk/Partnership/ssh_setup.sh\n/boot/config/plugins/varaverk/Plugin/Icons/Varaverk.png\n/boot/config/plugins/varaverk/Plugin/Manual-Plugin.md\n/boot/config/plugins/varaverk/Plugin/README-Plugin.md\n/boot/config/plugins/varaverk/Plugin/build.sh\n/boot/config/plugins/varaverk/Plugin/dev_install.sh\n/boot/config/plugins/varaverk/Plugin/dist/varaverk-2026.05.31-noarch-1.txz.sha256\n/boot/config/plugins/varaverk/Plugin/unraid/Partnership/containers.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/mover_stop.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/php_fpm_max_children.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh\n/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/user_scripts_stop.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/conf_populate.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/recreate_shares.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh\n/boot/config/plugins/varaverk/Plugin/unraid/Varaverk.page\n/boot/config/plugins/varaverk/Plugin/unraid/VaraverkSettings.page\n/boot/config/plugins/varaverk/Plugin/unraid/Watchdogs/System/webgui_watchdog.sh\n/boot/config/plugins/varaverk/Plugin/unraid/adapter.sh\n/boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/arrs.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/auth.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/board.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/cert.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/checklist.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/clearlock.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/conf_toggle.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/confform.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/config.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/create_api_key.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/docker.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/docker_action.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/dryrun.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/fallback.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/flag_toggle.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/log.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/media.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/monitor.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/monitor_fast.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/monitor_remote.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/movescript.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/partnership.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/partnership_ping.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/partnership_settings.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rawconf.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/readscript.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/recent.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/reorderarray.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_profiles.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_standalone.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_win_arrays.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/run.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/savefolders.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/scheduler.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/script.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/scriptinfo.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/settings.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/snapshot.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/status.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/stop.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/system.php\n/boot/config/plugins/varaverk/Plugin/unraid/api/watchdog.php\n/boot/config/plugins/varaverk/Plugin/unraid/css/varaverk.css\n/boot/config/plugins/varaverk/Plugin/unraid/event/disks_mounted/array_start_jobs\n/boot/config/plugins/varaverk/Plugin/unraid/event/disks_mounted/rebuild_cron\n/boot/config/plugins/varaverk/Plugin/unraid/event/disks_unmounting/array_stop_jobs\n/boot/config/plugins/varaverk/Plugin/unraid/icons/varaverk.png\n/boot/config/plugins/varaverk/Plugin/unraid/include/arrs.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/common.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/confform.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/config.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/docker.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/docker_folders.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/docs.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/fallback.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/media.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/monitor.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/vms.php\n/boot/config/plugins/varaverk/Plugin/unraid/include/watchdog.php\n/boot/config/plugins/varaverk/Plugin/unraid/js/varaverk.js\n/boot/config/plugins/varaverk/Plugin/unraid/pages/arrs.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/auth.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/docker.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/fallback.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/monitor.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/rsync.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/scheduler.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/settings.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/setup.php\n/boot/config/plugins/varaverk/Plugin/unraid/pages/watchdog.php\n/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh\n/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh\n/boot/config/plugins/varaverk/Plugin/varaverk.plg\n/boot/config/plugins/varaverk/README-User_Script_Plug-in.md\n/boot/config/plugins/varaverk/README.md\n/boot/config/plugins/varaverk/Rsync/Manual-Rsync.md\n/boot/config/plugins/varaverk/Rsync/README-Rsync.md\n/boot/config/plugins/varaverk/Rsync/rsync.sh\n/boot/config/plugins/varaverk/State_Files/container_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/docker_watchdog_failed.db\n/boot/config/plugins/varaverk/State_Files/docker_watchdog_manual_stop.db\n/boot/config/plugins/varaverk/State_Files/fallback_state.db\n/boot/config/plugins/varaverk/State_Files/network_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/partnership_offline_days.db\n/boot/config/plugins/varaverk/State_Files/resource_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/storage_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/system_watchdog_oom.db\n/boot/config/plugins/varaverk/State_Files/system_watchdog_reboots.db\n/boot/config/plugins/varaverk/State_Files/system_watchdog_state.db\n/boot/config/plugins/varaverk/State_Files/transcode_state.db\n/boot/config/plugins/varaverk/State_Files/varaverk_setup.db\n/boot/config/plugins/varaverk/State_Files/watchdog_appdata_growth.db\n/boot/config/plugins/varaverk/State_Files/watchdog_orch_hb.count\n/boot/config/plugins/varaverk/State_Files/watchdog_orchestrator.log\n/boot/config/plugins/varaverk/System_Essentials/Manual-System_Essentials.md\n/boot/config/plugins/varaverk/System_Essentials/README-System_Essentials.md\n/boot/config/plugins/varaverk/System_Essentials/clear_logs.sh\n/boot/config/plugins/varaverk/System_Essentials/conf_sync.sh\n/boot/config/plugins/varaverk/System_Essentials/docker_syslog_filter.sh\n/boot/config/plugins/varaverk/System_Essentials/inotify_tuning.sh\n/boot/config/plugins/varaverk/System_Essentials/rsync_stop.sh\n/boot/config/plugins/varaverk/System_Essentials/server_reboot.sh\n/boot/config/plugins/varaverk/Tools/Manual-Tools.md\n/boot/config/plugins/varaverk/Tools/README-Tools.md\n/boot/config/plugins/varaverk/Tools/bulk_permissions_repair.sh\n/boot/config/plugins/varaverk/Tools/claude_startup.sh\n/boot/config/plugins/varaverk/Tools/container_data_export.sh\n/boot/config/plugins/varaverk/Tools/docker_prune_images.sh\n/boot/config/plugins/varaverk/Tools/emby_database_repair.sh\n/boot/config/plugins/varaverk/Tools/emby_to_lidarr_sync.sh\n/boot/config/plugins/varaverk/Tools/emby_to_radarr_sync.sh\n/boot/config/plugins/varaverk/Tools/emby_to_sonarr_sync.sh\n/boot/config/plugins/varaverk/Tools/fallback_state_reset.sh\n/boot/config/plugins/varaverk/Tools/ramdisk_stop.sh\n/boot/config/plugins/varaverk/Tools/smart_long_test.sh\n/boot/config/plugins/varaverk/Tools/watchdog_skip_list_manager.sh\n/boot/config/plugins/varaverk/Tools/zfs_pool_scrub.sh\n/boot/config/plugins/varaverk/Transcodes/Manual-Transcoding.md\n/boot/config/plugins/varaverk/Transcodes/README-Transcoding.md\n/boot/config/plugins/varaverk/Transcodes/ramdisk_setup.sh\n/boot/config/plugins/varaverk/Transcodes/transcode_cleanup.sh\n/boot/config/plugins/varaverk/Transcodes/transcode_manager.sh\n/boot/config/plugins/varaverk/Watchdogs/Manual-Watchdogs.md\n/boot/config/plugins/varaverk/Watchdogs/README-Watchdogs.md\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/System/storage_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/docker_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/resource_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/stability_watchdog.sh\n/boot/config/plugins/varaverk/Watchdogs/system_watchdog.sh\n/boot/config/plugins/varaverk/common.sh\n/boot/config/plugins/varaverk/data/arr_cleanup_stats.db\n/boot/config/plugins/varaverk/data/arr_recovery_stats.db\n/boot/config/plugins/varaverk/data/bandwidth_history.db\n/boot/config/plugins/varaverk/data/container_restart_history.db\n/boot/config/plugins/varaverk/data/lidarr_discovery_history.db\n/boot/config/plugins/varaverk/data/lidarr_tracked.count\n/boot/config/plugins/varaverk/data/radarr_discovery_history.db\n/boot/config/plugins/varaverk/data/sonarr_discovery_history.db\n/boot/config/plugins/varaverk/data/transcode_daily.db\n/boot/config/plugins/varaverk/docker_folders.json\n/boot/config/plugins/varaverk/git_pull_execute.sh\n/boot/config/plugins/varaverk/install.log\n/boot/config/plugins/varaverk/load_config.sh\n/boot/config/plugins/varaverk/schedule.json\n/boot/config/plugins/varaverk/varaverk-2026.05.31-noarch-1.txz\n/boot/config/plugins/varaverk/varaverk.cfg\n/boot/config/plugins/varaverk/varaverk.cron","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"30715d8a-0eec-437d-b7a8-5815668cec22","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"last-prompt","lastPrompt":"NPM auth failed — check credentials in host conf","leafUuid":"7a44cd18-ae03-4c1a-891c-37daa0201ef7","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"ai-title","aiTitle":"Troubleshoot NPM authentication failure","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"mode","mode":"normal","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"permission-mode","permissionMode":"default","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"parentUuid":"7a44cd18-ae03-4c1a-891c-37daa0201ef7","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_0158ToqvAAudKdZNWhTNLoMn","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01GhiZLDnsMmX856x7Z4biyk","name":"Read","input":{"file_path":"/boot/config/plugins/varaverk/Configurations/host1.conf"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7198,"cache_read_input_tokens":18241,"output_tokens":123,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":7198,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":123,"cache_read_input_tokens":18241,"cache_creation_input_tokens":7198,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7198},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRkJPRTEYYWEAtHwFQV","type":"assistant","uuid":"0c5f94b8-15d6-40c2-be8b-ffcc6daf8cc1","timestamp":"2026-06-06T17:48:52.322Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"0c5f94b8-15d6-40c2-be8b-ffcc6daf8cc1","isSidechain":false,"promptId":"0a108bc8-cb46-4a98-91cc-27f1cab8a949","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01GhiZLDnsMmX856x7Z4biyk","type":"tool_result","content":"1\t#!/bin/bash\n2\t# ==============================================================================================\n3\t# ========================== HOST1 CONFIGURATION — unRAID-Gmer4Lfe ============================\n4\t# ==============================================================================================\n5\t# HOST1-specific variables — credentials, container names, share paths, failover lists.\n6\t# Sourced after master.conf — values here extend shared profile arrays and add HOST1-specific\n7\t# identity, credentials, and container configuration.\n8\t#\n9\t# Sparse checkout (git) ensures HOST2 never receives this file.\n10\t# HOST2 never sees HOST1 credentials — clean separation at the file level.\n11\t#\n12\t# DO NOT put shared config here — thresholds, toggles, profiles belong in master.conf.\n13\t# DO NOT put HOST2 variables here — they belong in host2.conf.\n14\t#\n15\t# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────\n16\t#\n17\t# ── IDENTITY & CONNECTIVITY ────────────────────────────────────────────────────────────────\n18\t# IDENTITY hostname, SSH key\n19\t# EMBY container name, URL, API key\n20\t# NOTIFICATIONS Discord webhook\n21\t# PARTNERSHIP auth containers, backup paths\n22\t#\n23\t# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────\n24\t# DAILY SYNC SHARES media shares HOST1 owns and pushes to HOST2\n25\t# WEEKLY SYNC SHARES appdata shares synced weekly (Sunday 2:30am)\n26\t# CRITICAL SYNC SHARES appdata shares synced every 30 minutes\n27\t# BACKUP VERIFY shares for checksum verification against remote\n28\t# HOST1 RSYNC PROFILE host1-appdata profile for HOST1-specific appdata syncs\n29\t#\n30\t# ── DOCKER ─────────────────────────────────────────────────────────────────────────────────\n31\t# DOCKER DAILY RESTART containers restarted daily\n32\t# DOCKER WEEKLY RESTART containers restarted weekly\n33\t# DOCKER WATCHDOG memory limits, health URLs, required containers, ignore list\n34\t# DOCKER NETWORK CONNECT networks and containers for docker_network_connect.sh\n35\t#\n36\t# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────\n37\t# DDNS DDNS containers managed by HOST1\n38\t# INTERNET LOSS containers stopped when internet is lost\n39\t# FALLBACK TIERS what HOST1 runs for HOST2 per tier\n40\t# TIER DELAYS how long HOST1 must be down before each tier activates on HOST2\n41\t# RSYNC WRITEBACK HOST1 appdata synced back on handback\n42\t#\n43\t# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────\n44\t# MEDIA PERMISSIONS share list for media_shares_permissions.sh\n45\t# MEDIA CLEANER folder lists for media_cleaner.sh\n46\t#\n47\t# ── MONITORS ───────────────────────────────────────────────────────────────────────────────\n48\t# CERTIFICATE MONITOR domains checked for SSL expiry\n49\t# SMART HEALTH drives to skip in SMART monitoring\n50\t# ZFS REPORT pools to exclude from ZFS health report\n51\t#\n52\t# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────\n53\t# TRANSCODES ramdisk size, thresholds, SSD path, server array\n54\t#\n55\t# ── ARR STACK ──────────────────────────────────────────────────────────────────────────────\n56\t# DOWNLOADERS slskd, SABnzbd, qBittorrent credentials and URLs\n57\t# LIDARR URL, API key, path map\n58\t# SONARR URL, API key, path map\n59\t# RADARR URL, API key, path map\n60\t# ARR RECOVERY per-arr recovery toggles\n61\t#\n62\t# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────\n63\t# SYSTEM WATCHDOG per-host check toggles and NIC configuration\n64\t#\n65\t# ── RESOURCE MANAGER ───────────────────────────────────────────────────────────────────────\n66\t# RESOURCE MANAGER containers paused/stopped under memory pressure\n67\t#\n68\t# ==============================================================================================\n69\t\n70\t# ==============================================================================================\n71\t# ── IDENTITY & CONNECTIVITY ───────────────────────────────────────────────────────────────────\n72\t# ==============================================================================================\n73\t\n74\t# ━━━ Identity ━━━\n75\t# HOST1 hostname lives in master.conf (not a credential — safe for all servers).\n76\t# SSH key used for all server-to-server operations — rsync, failover container commands.\n77\t# Must be in /root/.ssh/ and authorised in HOST2's /root/.ssh/authorized_keys.\n78\t HOST1_SSH_KEY=\"/root/.ssh/gmer4lfe_rsync_automation\"\n79\t HOST1_OWNER=\"gmer4lfe\"\n80\t HOST1_OWNER_EMAIL=\"gmer4lfe@gmail.com\"\n81\t\n82\t# ━━━ Emby ━━━\n83\t# Referenced by transcode_manager.sh, emby_session_report.sh, emby_database_repair.sh,\n84\t# weekly_sync_maintenance.sh, and HOST1_TRANSCODE_SERVERS below.\n85\t# API key: Emby Dashboard → API Keys → + New Key\n86\t HOST1_EMBY_CONTAINER=\"Emby\"\n87\t HOST1_EMBY_URL=\"http://localhost:8096\"\n88\t HOST1_EMBY_API_KEY=\"0c27448d93a7431f9ac63569f7655829\"\n89\t\n90\t# ━━━ Jellyfin ━━━\n91\t# API key: Jellyfin Dashboard → Administration → API Keys → + New Key\n92\t HOST1_JELLYFIN_CONTAINER=\"Jellyfin\"\n93\t HOST1_JELLYFIN_URL=\"http://localhost:8095\"\n94\t HOST1_JELLYFIN_API_KEY=\"4e820e7df74c4933acec212b1996314e\"\n95\t\n96\t# ━━━ Gitea ━━━\n97\t# Personal access token for gitea_ssh_setup.sh — registers this server's SSH public key\n98\t# with Gitea so git operations use key auth instead of passwords.\n99\t# Create in Gitea: Settings → Applications → Generate Token → scope: write:user\n100\t HOST1_GITEA_API_TOKEN=\"\"\n101\t\n102\t# ━━━ Notifications ━━━\n103\t# Discord webhook — leave blank to disable.\n104\t# Per-host so HOST1 and HOST2 can post to different channels or only one server notifies.\n105\t HOST1_DISCORD_WEBHOOK=\"\"\n106\t\n107\t# ━━━ Partnership ━━━\n108\t# HOST1 is always the owner (source of truth) unless --transfer has been run.\n109\t# See README-Partnership.md and master.conf PARTNERSHIP section for full lifecycle docs.\n110\t\n111\t# Auth containers reconfigured on onboard/offboard.\n112\t# Format: \"ContainerName|WebUIPort\"\n113\t# On onboard → WebUI pointed at owner's Tailscale IP (mirror clicks NPM, gets owner's auth)\n114\t# On offboard → WebUI pointed back at localhost\n115\t HOST1_PARTNERSHIP_AUTH_WEBUIS=(\n116\t \"NginxProxyManager|81\"\n117\t \"Lldap-Gmer4Lfe|17170\"\n118\t \"Authelia|9091\"\n119\t \"Authelia-Secondary|9092\"\n120\t )\n121\t\n122\t# XML templates (from this server's templates-user/) pushed to mirror during onboard.\n123\t# These become the mirror's active auth stack, backed by the rsync-synced appdata.\n124\t# Update filename if Lldap is renamed to drop the host suffix.\n125\t HOST1_PARTNERSHIP_AUTH_STACK=(\n126\t # Dependencies first — Mariadb/Redis must be healthy before Authelia starts\n127\t \"my-Mariadb-Authelia.xml\"\n128\t \"my-Mariadb-Authelia-Secondary.xml\"\n129\t \"my-Redis-Authelia.xml\"\n130\t \"my-Redis-Authelia-Secondary.xml\"\n131\t # Auth apps — deployed after their deps are confirmed healthy\n132\t \"my-Authelia.xml\"\n133\t \"my-Authelia-Secondary.xml\"\n134\t \"my-NginxProxyManager.xml\"\n135\t \"my-Lldap-Gmer4Lfe.xml\"\n136\t # Source of truth — must be available on HOST2 independently of the auth stack\n137\t \"my-Gitea.xml\"\n138\t )\n139\t\n140\t# XML templates pushed to mirror for the arr stack during onboard.\n141\t# Deps (e.g. databases) first if any — same ordering rule as auth stack.\n142\t HOST1_PARTNERSHIP_ARR_STACK=(\n143\t # \"my-Sonarr.xml\"\n144\t # \"my-Radarr.xml\"\n145\t # \"my-Lidarr.xml\"\n146\t # \"my-Prowlarr.xml\"\n147\t # \"my-Bazarr.xml\"\n148\t )\n149\t\n150\t# Paths HOST2 should collect during the grace window after offboard.\n151\t# Notified on offboard — no auto-deletion, HOST2 must collect manually within PARTNERSHIP_GRACE_HOURS.\n152\t HOST1_PARTNERSHIP_MIRROR_BACKUPS=(\n153\t # \"/mnt/user/appdata-Fallback/Jayred365-Emby\"\n154\t )\n155\t\n156\t# Containers parked on this server when partnership is active.\n157\t# Stopped on onboard (owner deploys its stack instead), restarted on offboard.\n158\t HOST1_PARTNERSHIP_OWN_CONTAINERS=(\n159\t # \"Emby\"\n160\t # \"NginxProxyManager\"\n161\t )\n162\t\n163\t# Emby admin provisioning — toggle is owner-only, credentials are per-host.\n164\t# Owner enables/disables the feature. Each host sets the account they want on the shared Emby.\n165\t# On onboard: owner reads mirror's HOST*_PARTNERSHIP_EMBY_ADMIN_* and creates that account.\n166\t# On offboard: account is deleted. Username collision → onboard exits with error.\n167\t HOST1_PARTNERSHIP_PROVISION_EMBY_ADMIN=false # owner controls whether Emby is shared\n168\t HOST1_PARTNERSHIP_EMBY_PORT=8096\n169\t HOST1_PARTNERSHIP_EMBY_ADMIN_USER=\"\" # this server's desired Emby username\n170\t HOST1_PARTNERSHIP_EMBY_ADMIN_PASS=\"\" # this server's desired Emby password\n171\t\n172\t# ==============================================================================================\n173\t# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────\n174\t# ==============================================================================================\n175\t\n176\t# ━━━ Daily Sync Shares ━━━\n177\t# Shares HOST1 pushes to all other nodes every night (1am via daily_sync_maintenance.sh).\n178\t# Mesh model: every node pushes every media share — no ownership, no mirrors.\n179\t# arr_sync ensures all arr libraries converge (union). rsync spreads files (additive, no --delete).\n180\t# arr_cleanup removes true orphans based on local arr state.\n181\t# Any node can download content to any share — it propagates to all nodes on the next cycle.\n182\t# Nextcloud is intentionally one-directional (HOST1→HOST2 offsite backup — not arr-managed).\n183\t# Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed.\n184\t# For shares needing container stops or custom options — add a profile in master.conf.\n185\t HOST1_DAILY_SYNC_SHARES=(\n186\t /mnt/user/Books\n187\t /mnt/user/Intros\n188\t /mnt/user/Kids_Movies\n189\t /mnt/user/Kids_Tv_Shows\n190\t /mnt/user/Movies\n191\t /mnt/user/Music\n192\t /mnt/user/Music_Videos\n193\t /mnt/user/Nextcloud\n194\t /mnt/user/stand-up_comedy\n195\t /mnt/user/Sports\n196\t /mnt/user/Tv_Shows\n197\t /mnt/user/Anime_Shows-Old\n198\t /mnt/user/Anime_Movies-Old\n199\t /mnt/user/Anime_Movies\n200\t /mnt/user/Anime_Shows\n201\t )\n202\t\n203\t# Personal encrypted shares — synced for offsite backup, independent of media shares.\n204\t# ZFS encrypted at dataset level — remote receives encrypted blocks, cannot read content.\n205\t# See README-Rsync_Setup.md for ZFS encryption setup before uncommenting.\n206\t HOST1_PERSONAL_SHARES=(\n207\t # /mnt/user/HOST1-Personal # uncomment after creating encrypted dataset\n208\t )\n209\t\n210\t# ━━━ Weekly Sync Shares ━━━\n211\t# Appdata shares synced during the weekly maintenance window (Sunday 2:30am).\n212\t# Containers stopped both sides before sync — full clean state guaranteed.\n213\t# Profiles drive container stops, excludes, and options — configured in master.conf RSYNC section.\n214\t# Order matters — Emby first (larger transfer), then Critical-Data (auth stack).\n215\t HOST1_WEEKLY_SYNC_SHARES=(\n216\t \"/mnt/user/Media_Server/Emby\" # emby profile — full clean mirror\n217\t \"/mnt/user/appdata-Fallback/Critical-Data\" # critical-data profile — auth stack\n218\t )\n219\t\n220\t# ━━━ Intermediate Sync Shares ━━━\n221\t# Shares synced every 4 hours by intermediate_sync_maintenance.sh.\n222\t# Uses DEFAULT_RSYNC_OPTS (no --delete) — for sub-daily propagation of metadata or watch state.\n223\t# Full media share sync stays in the daily window. Leave empty to skip mid-day rsync entirely.\n224\t HOST1_INTERMEDIATE_SYNC_SHARES=(\n225\t # Add shares here to enable mid-day rsync\n226\t # Example: \"/mnt/user/Emby_Metadata\"\n227\t )\n228\t\n229\t# ━━━ Critical Sync Shares ━━━\n230\t# Appdata shares synced every 30 minutes by critical_sync_maintenance.sh.\n231\t# Format: \"/path/to/share\" or \"/path/to/share|profile-name\"\n232\t# Order matters — Critical-Data first (auth stack), then Emby dirty sync.\n233\t HOST1_CRITICAL_SYNC_SHARES=(\n234\t \"/mnt/user/appdata-Fallback/Critical-Data|critical-fallback\" # auth dirty sync — stays running\n235\t \"/mnt/user/Media_Server/Emby|emby-fallback\" # Emby dirty sync — stays running\n236\t )\n237\t\n238\t# ━━━ Backup Verify ━━━\n239\t# Shares verified by backup_verify.sh — random file checksum comparison against remote.\n240\t# Leave empty to use HOST1_DAILY_SYNC_SHARES automatically.\n241\t# Sample size and minimum file size defined in master.conf.\n242\t HOST1_BACKUP_VERIFY_SHARES=(\n243\t # leave empty to use HOST1_DAILY_SYNC_SHARES automatically\n244\t )\n245\t\n246\t# ━━━ HOST1 Rsync Profile — host1-appdata ━━━\n247\t# HOST1-specific appdata sync profile — extends the shared PROFILE_* arrays in master.conf.\n248\t# Use for appdata unique to HOST1 (Organizrv2, VaultWarden, UptimeKuma etc.)\n249\t# Shared appdata (auth stack, Emby) use dedicated profiles defined in master.conf.\n250\t# Run manually: bash Rsync/rsync.sh /mnt/user/appdata-Fallback/HOST1-Appdata --profile=host1-appdata\n251\t PROFILE_RSYNC_OPTS[host1-appdata]=\"-av --info=progress2 --bwlimit=${PROFILE_BW_LIMIT[host1-appdata]:-8000}\"\n252\t PROFILE_BW_LIMIT[host1-appdata]=8000\n253\t PROFILE_RETRY_COUNT[host1-appdata]=3\n254\t PROFILE_SLEEP[host1-appdata]=300\n255\t PROFILE_CRITICAL_CONTAINER_NAMES[host1-appdata]=\"Organizrv2-Gmer4Lfe UptimeKuma-Gmer4Lfe VaultWarden-Gmer4Lfe\"\n256\t PROFILE_DELAYED_CONTAINERS[host1-appdata]=\"\"\n257\t PROFILE_CONTAINER_DELAY[host1-appdata]=5\n258\t PROFILE_EXCLUDE_DIRS[host1-appdata]=\"logs *.tmp\"\n259\t\n260\t# ==============================================================================================\n261\t# ── DOCKER ────────────────────────────────────────────────────────────────────────────────────\n262\t# ==============================================================================================\n263\t\n264\t# ━━━ Docker Daily Restart ━━━\n265\t# Containers restarted every day via DAILY_MAINTENANCE_SCRIPTS.\n266\t# Dispatcharr degrades over time without restart — daily is intentional, not just housekeeping.\n267\t# Order matters — auth stack first, then media services.\n268\t HOST1_DAILY_RESTART_CONTAINERS=(\n269\t \"NginxProxyManager\"\n270\t \"Lldap-Gmer4Lfe\"\n271\t \"Authelia\"\n272\t \"Authelia-Secondary\"\n273\t \"Dispatcharr-Iptv-Users\"\n274\t \"Dispatcharr\" # Live TV scheduler — degrades without daily restart\n275\t \"Dispatcharr-Basic\"\n276\t \"ErsatzTV-Emby\"\n277\t \"Slskd\" # Soulseek connection drops after extended uptime; restart refreshes share index\n278\t )\n279\t\n280\t# ━━━ Docker Weekly Restart ━━━\n281\t# Less critical services restarted weekly via WEEKLY_MAINTENANCE_SCRIPTS (Sunday 2:30am).\n282\t# Containers already stopped for weekly sync — restart adds zero extra downtime.\n283\t HOST1_WEEKLY_RESTART_CONTAINERS=(\n284\t \"NextCloud\"\n285\t \"Organizrv2-Gmer4Lfe\"\n286\t \"AdGuard-Home\"\n287\t \"Immich-Gmer4Lfe\"\n288\t )\n289\t\n290\t# ━━━ Docker Watchdog ━━━\n291\t# Per-HOST1 container configuration for docker_watchdog.sh.\n292\t# Shared thresholds and toggles live in master.conf.\n293\t\n294\t# Memory hard limits in MB — immediate restart if exceeded.\n295\t# Set at \"container is clearly broken\" not \"container is busy\".\n296\t# 20GB=20480 18GB=18432 16GB=16384 12GB=12288 8GB=8192 4GB=4096 2GB=2048 1GB=1024\n297\t declare -A HOST1_WATCHDOG_CONTAINERS=(\n298\t [\"Emby\"]=20480 # 20GB — large library + active transcodes\n299\t [\"LidaTube\"]=6144 # 6GB — memory leak over time\n300\t [\"Tdarr\"]=6144 # 6GB — encoding is memory intensive\n301\t [\"Code-Server\"]=1024 # 1GB — should never need more\n302\t )\n303\t\n304\t# HTTP health check URLs — checked every cycle, strike system before restart.\n305\t# Only add containers with a meaningful web interface to check.\n306\t declare -A HOST1_WATCHDOG_CONTAINER_URLS=(\n307\t [\"Emby\"]=\"http://localhost:8096\"\n308\t )\n309\t\n310\t# Required containers — must always be running on HOST1.\n311\t# Strike system before restart — repeated failures go on skip list, auto-clears on recovery.\n312\t# Listed in dependency order — dependencies before dependents.\n313\t HOST1_WATCHDOG_REQUIRED_CONTAINERS=(\n314\t \"NginxProxyManager\"\n315\t \"Lldap-Gmer4Lfe\"\n316\t \"Mariadb-Authelia\"\n317\t \"Mariadb-Authelia-Secondary\"\n318\t \"Redis-Authelia\"\n319\t \"Redis-Authelia-Secondary\"\n320\t \"Authelia\"\n321\t \"Authelia-Secondary\"\n322\t )\n323\t\n324\t# Containers to skip in Tier 2 global scan — legitimately stopped or frequently restarting.\n325\t# Watchdog leaves these alone entirely — no restart attempts, no crash loop tracking.\n326\t HOST1_WATCHDOG_SCAN_IGNORE=(\n327\t \"DashGate\"\n328\t \"PIA-WG-Config-Generator\"\n329\t \"Aperture\"\n330\t \"Aperture-Kids\"\n331\t \"pgvector-18-Apeture-Kids\"\n332\t \"Pgvector18-Aperture\"\n333\t \"emby-test\" # broken test container (exit 127 — bad image)\n334\t )\n335\t\n336\t# Dependency ordering — skip restarting a container if its dependency is also down.\n337\t# Prevents watchdog from restarting Authelia before Mariadb is back up.\n338\t# SPACE-SEPARATED STRINGS — converted to array at runtime.\n339\t declare -A HOST1_WATCHDOG_DEPENDENCIES=(\n340\t [\"Authelia\"]=\"Mariadb-Authelia Redis-Authelia\"\n341\t [\"Authelia-Secondary\"]=\"Mariadb-Authelia Redis-Authelia-Secondary\"\n342\t [\"NextCloud\"]=\"Postgres-NextCloud\"\n343\t )\n344\t\n345\t# Per-container appdata growth suppress ceilings in MB.\n346\t# ONLY needed in specific cases — growth rate detection covers all containers automatically.\n347\t# Use this when a container legitimately has large stable data and you want to guarantee\n348\t# it never triggers a false-positive growth alert. Growth warnings are suppressed while the\n349\t# container's dir stays below this ceiling; above it, warnings resume as normal.\n350\t# 50GB=51200 25GB=25600 20GB=20480 15GB=15360 10GB=10240 5GB=5120\n351\t declare -A HOST1_WATCHDOG_APPDATA_SIZES=(\n352\t [\"Tdarr\"]=\"25600\" # 25GB — transcode cache grows legitimately during active jobs\n353\t [\"7dtd\"]=\"20480\" # 20GB — game server world data, expected to be large\n354\t )\n355\t\n356\t# ━━━ Network Watchdog ━━━\n357\t# Host-specific connectivity config for Watchdogs/System/network_watchdog.sh.\n358\t HOST1_NETWORK_WATCHDOG_DDNS_DOMAIN=\"gmer4lfe.com\"\n359\t HOST1_NETWORK_WATCHDOG_DDNS_CONTAINER=\"Gmer4Lfe.com\"\n360\t HOST1_NETWORK_WATCHDOG_NPM_URL=\"https://gmer4lfe.com\"\n361\t\n362\t# ━━━ Docker Network Connect ━━━\n363\t# Containers connected to custom networks at array start by docker_network_connect.sh.\n364\t# Networks created if they don't exist — idempotent, safe to re-run.\n365\t HOST1_NETWORK_CONNECT_CONTAINERS=(\n366\t \"memcached\"\n367\t \"Npm-CrowdSec\"\n368\t )\n369\t\n370\t HOST1_NETWORK_CONNECT_NETWORKS=(\n371\t \"high-availability\"\n372\t )\n373\t\n374\t# ==============================================================================================\n375\t# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────\n376\t# ==============================================================================================\n377\t\n378\t# ━━━ DDNS ━━━\n379\t# DDNS containers HOST1 manages — started/stopped by fallback.sh per DDNS absolute rules:\n380\t# Internet loss → stop immediately\n381\t# Failover → HOST2 starts HOST1's DDNS as Tier 1 (before any other containers)\n382\t# Handback → stop HOST1's DDNS on HOST2 → rsync → start containers → start local DDNS last\n383\t HOST1_DDNS_CONTAINERS=(\n384\t \"Gmer4Lfe.com\"\n385\t )\n386\t\n387\t# ━━━ Internet Loss ━━━\n388\t# Containers stopped immediately on HOST1 when internet connection is lost.\n389\t# Prevents external-facing services from operating without connectivity.\n390\t FALLBACK_HOST1_STOP_ON_NO_NET=(\n391\t \"Gmer4Lfe.com\"\n392\t )\n393\t\n394\t# ━━━ Fallback Tiers — HOST1 Runs for HOST2 ━━━\n395\t# Containers HOST1 starts when HOST2 goes down.\n396\t# Tier 1 is always immediate — vital services cannot wait.\n397\t# Higher tiers activate after HOST2_TIER*_DELAY minutes (set in host2.conf).\n398\t FALLBACK_HOST1_COVERS_HOST2_TIER1=(\n399\t \"Gmer4Lfe.us\"\n400\t \"VaultWarden-Jayred365\"\n401\t )\n402\t\n403\t FALLBACK_HOST1_COVERS_HOST2_TIER2=(\n404\t # \"container-placeholder\"\n405\t )\n406\t\n407\t FALLBACK_HOST1_COVERS_HOST2_TIER3=(\n408\t # \"container-placeholder\"\n409\t )\n410\t\n411\t FALLBACK_HOST1_COVERS_HOST2_TIER4=(\n412\t # \"container-placeholder\"\n413\t )\n414\t\n415\t# ━━━ Tier Delays — HOST1's Containers on HOST2 ━━━\n416\t# How long HOST1 must be down before each tier activates on HOST2 — in minutes.\n417\t# Tier 1 is always immediate — no delay var needed.\n418\t HOST1_TIER2_DELAY=240 # 4 hours — NextCloud, Immich\n419\t HOST1_TIER3_DELAY=720 # 12 hours — secondary services\n420\t HOST1_TIER4_DELAY=1440 # 24 hours — arrs + downloaders\n421\t\n422\t# ━━━ Rsync Writeback — HOST1 Appdata Back on Handback ━━━\n423\t# Syncs HOST1 appdata BACK to HOST1 when it comes back online after a failover.\n424\t# Containers stopped before writeback — clean source, no competing writes.\n425\t#\n426\t# HOST1_TIER1_WRITEBACK_DELAY: short outages skip Tier 1 writeback — primary state\n427\t# is more reliable than dirty sync data for brief outages.\n428\t HOST1_TIER1_WRITEBACK_DELAY=60 # skip Emby writeback if outage under 1hr\n429\t\n430\t# Tier 4 automatically syncs HOST1_DAILY_SYNC_SHARES — only list paths NOT in that array.\n431\t FALLBACK_HOST1_WRITEBACK_TIER1=(\n432\t \"/mnt/user/Media_Server/Emby\" # watch states built up during outage\n433\t )\n434\t\n435\t FALLBACK_HOST1_WRITEBACK_TIER2=(\n436\t \"/mnt/user/appdata-Fallback/Important-Data\" # NextCloud + Postgres — files added during outage\n437\t )\n438\t\n439\t FALLBACK_HOST1_WRITEBACK_TIER3=(\n440\t # \"location-placeholder\"\n441\t )\n442\t\n443\t FALLBACK_HOST1_WRITEBACK_TIER4=(\n444\t \"/mnt/user/appdata-Fallback/Arrs_Stack\" # arr databases — downloads queued during outage\n445\t )\n446\t\n447\t# ==============================================================================================\n448\t# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────\n449\t# ==============================================================================================\n450\t\n451\t# ━━━ Media Permissions ━━━\n452\t# Shares that media_shares_permissions.sh applies PERMISSIONS_MODE and PERMISSIONS_OWNER to.\n453\t# Runs first in DAILY_MAINTENANCE_SCRIPTS — arr cleanup depends on correct ownership.\n454\t HOST1_MEDIA_PERMISSION_SHARES=(\n455\t /mnt/user/Anime_Movies\n456\t /mnt/user/Anime_Movies-Old\n457\t /mnt/user/Anime_Shows\n458\t /mnt/user/Anime_Shows-Old\n459\t /mnt/user/appcache\n460\t /mnt/user/Books\n461\t /mnt/user/Downloads\n462\t /mnt/user/Games\n463\t /mnt/user/Intros\n464\t /mnt/user/Kids_Movies\n465\t /mnt/user/Kids_Tv_Shows\n466\t /mnt/user/Movie_Recordings\n467\t /mnt/user/Movies\n468\t /mnt/user/Music\n469\t /mnt/user/Music_Videos\n470\t /mnt/user/Photo\n471\t /mnt/user/Sports\n472\t /mnt/user/stand-up_comedy\n473\t /mnt/user/Temp_Storage\n474\t /mnt/user/Tv_Recordings\n475\t /mnt/user/Tv_Shows\n476\t /mnt/user/YouTube\n477\t )\n478\t\n479\t# ━━━ Media Cleaner ━━━\n480\t# Folder lists for media_cleaner.sh — two profiles: anime and media.\n481\t# File patterns shared across all servers — defined in master.conf.\n482\t# Called via DAILY_MAINTENANCE_SCRIPTS. Run manually: Media/media_cleaner.sh anime|media\n483\t HOST1_ANIME_CLEAN_FOLDERS=(\n484\t /mnt/user/Anime_Movies\n485\t /mnt/user/Anime_Movies-Old\n486\t /mnt/user/Anime_Shows\n487\t /mnt/user/Anime_Shows-Old\n488\t )\n489\t\n490\t HOST1_MEDIA_CLEAN_FOLDERS=(\n491\t /mnt/user/Kids_Movies\n492\t /mnt/user/Kids_Tv_Shows\n493\t /mnt/user/Movies\n494\t /mnt/user/Music\n495\t /mnt/user/Sports\n496\t /mnt/user/stand-up_comedy\n497\t /mnt/user/Tv_Shows\n498\t )\n499\t\n500\t# ==============================================================================================\n501\t# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────\n502\t# ==============================================================================================\n503\t\n504\t# ━━━ Certificate Monitor ━━━\n505\t# Domains checked via direct openssl connection — not relying on NPM's certificate state.\n506\t# Checks the actual certificate served, not what NPM thinks it has.\n507\t# Thresholds (CERT_WARN_DAYS, CERT_CRIT_DAYS) defined in master.conf.\n508\t HOST1_CERT_MONITOR_DOMAINS=(\n509\t \"Gmer4Lfe.com\"\n510\t \"Gmer4Lfe.us\"\n511\t )\n512\t\n513\t# ━━━ SMART Health ━━━\n514\t# Drives skipped in SMART attribute monitoring — hardware is server-specific.\n515\t# Thresholds read from dynamix.cfg at runtime — fallbacks in master.conf.\n516\t HOST1_SMART_IGNORE_DRIVES=(\n517\t \"sda\" # boot USB — SMART not meaningful on flash drives\n518\t )\n519\t\n520\t# ━━━ ZFS Report ━━━\n521\t# Pools excluded from the weekly ZFS health report — reduces noise from single-disk array pools.\n522\t# These are individual array disks formatted as ZFS — converting to XFS over time via unBalance.\n523\t# Pool health thresholds defined in master.conf.\n524\t HOST1_ZFS_REPORT_IGNORE_POOLS=(\n525\t \"disk5\"\n526\t \"disk6\"\n527\t \"disk8\"\n528\t \"disk9\"\n529\t \"disk10\"\n530\t )\n531\t\n532\t# ==============================================================================================\n533\t# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────\n534\t# ==============================================================================================\n535\t\n536\t# Ramdisk size ceiling — tmpfs only uses RAM actually needed, not the full size upfront.\n537\t# Real-world: 9 streams peaked at ~5.5GB — 10G gives generous headroom on 128GB RAM.\n538\t HOST1_RAMDISK_SIZE=\"10G\"\n539\t\n540\t# Usage thresholds — coupled to HOST1_RAMDISK_SIZE, adjust all three together if size changes.\n541\t# Hysteresis gap (8.5 - 7 = 1.5GB) prevents flip-flop between ramdisk and SSD.\n542\t HOST1_RAMDISK_WARN_GB=8.5 # flip to SSD when ramdisk usage reaches this\n543\t HOST1_RAMDISK_LOW_GB=7 # flip back to ramdisk when usage drops to this\n544\t\n545\t# SSD fallback path — where transcodes land when ramdisk exceeds HOST1_RAMDISK_WARN_GB.\n546\t# Must be on cache pool — array disks too slow for active transcode writes.\n547\t HOST1_TRANSCODE_SSD=\"/mnt/cache/Temp_Storage/Emby/Transcodes/\"\n548\t\n549\t# Media servers sharing the ramdisk transcode space on HOST1.\n550\t# Format: \"ContainerName|URL|APIKey|Type\" — Type: emby | jellyfin | plex\n551\t# Entries with placeholder API keys are skipped automatically.\n552\t# ⚠️ Tdarr does NOT belong here — keep Tdarr on SSD, not ramdisk.\n553\t HOST1_TRANSCODE_SERVERS=(\n554\t \"${HOST1_EMBY_CONTAINER}|${HOST1_EMBY_URL}|${HOST1_EMBY_API_KEY}|emby\"\n555\t \"${HOST1_JELLYFIN_CONTAINER}|${HOST1_JELLYFIN_URL}|${HOST1_JELLYFIN_API_KEY}|jellyfin\"\n556\t )\n557\t\n558\t# ==============================================================================================\n559\t# ── ARR STACK ─────────────────────────────────────────────────────────────────────────────────\n560\t# ==============================================================================================\n561\t# Used by arr cleanup scripts and arrs_failed_stalled_recovery.sh.\n562\t# detect_hosts() selects HOST1 vars when running on HOST1.\n563\t#\n564\t# PATH MAPS — container path → host path translation.\n565\t# Arr stores file paths using container-internal paths — scripts need host paths to scan.\n566\t# Add one entry per root folder in arr Settings → Media Management → Root Folders.\n567\t\n568\t# ━━━ Downloaders ━━━\n569\t# Used by downloaders_reset.sh — runs every 30min via CRITICAL_MAINTENANCE_SCRIPTS.\n570\t# Clears stuck states, purges old history, prepares each client for a clean cycle.\n571\t\n572\t# slskd — clears stuck searches, dead transfers, purges expired failed imports.\n573\t# SLSKD_FAILED_IMPORTS_DIR: where Soularr moves albums Lidarr rejected.\n574\t HOST1_SLSKD_URL=\"http://localhost:8980\"\n575\t HOST1_SLSKD_API_KEY=\"4bF9kL2mNpQrT7vWxYz1A3dEgHjKoRsU\"\n576\t HOST1_SLSKD_FAILED_IMPORTS_DIR=\"/mnt/user/Temp_Storage/Slskd/completed/failed_imports\"\n577\t\n578\t# SABnzbd\n579\t HOST1_SABNZBD_URL=\"http://localhost:8180\"\n580\t HOST1_SABNZBD_API_KEY=\"8bfefe41d83b4d50883e32859b55ca9a\"\n581\t\n582\t# qBittorrent — deleteFiles=false removes torrent from qBit but leaves files on disk.\n583\t# Radarr/Sonarr manage actual files independently.\n584\t HOST1_QBIT_URL=\"http://localhost:8080\"\n585\t HOST1_QBIT_USERNAME=\"root\"\n586\t HOST1_QBIT_PASSWORD=\"Stay0utD!ck\"\n587\t\n588\t# ━━━ Lidarr — HOST1 only ━━━\n589\t# HOST2 does not run Lidarr — HOST1_LIDARR_RECOVERY flag handles the exit cleanly.\n590\t HOST1_LIDARR_URL=\"http://localhost:8686\"\n591\t HOST1_LIDARR_API_KEY=\"b2977e71ef074bc0a0529d9fcce3b2dc\"\n592\t HOST1_LIDARR_MUSIC_ROOT=\"/mnt/user/Music-New\"\n593\t HOST1_FANART_API_KEY=\"Yd7147a43b692df0b364b94dc47efb81\"\n594\t HOST1_LASTFM_API_KEY=\"be6dc169c33ae263e690c30d18b7491d\"\n595\t\n596\t declare -A HOST1_LIDARR_PATH_MAP=(\n597\t [\"/ext-music\"]=\"/mnt/user/Music-New\"\n598\t )\n599\t\n600\t# ━━━ Sonarr ━━━\n601\t HOST1_SONARR_URL=\"http://localhost:8989\"\n602\t HOST1_SONARR_API_KEY=\"130decd3db5b4c25afad64864cd03f9f\"\n603\t HOST1_SONARR_TV_ROOT=\"/mnt/user/Tv_Shows\"\n604\t\n605\t# Note: stand-up_comedy in both Sonarr + Radarr — TV specials and movie specials, one folder\n606\t declare -A HOST1_SONARR_PATH_MAP=(\n607\t [\"/tv\"]=\"/mnt/user/Tv_Shows\"\n608\t [\"/ext-standup-comedy\"]=\"/mnt/user/stand-up_comedy\"\n609\t [\"/kids tv\"]=\"/mnt/user/Kids_Tv_Shows\"\n610\t [\"/ext-anime-shows\"]=\"/mnt/user/Anime_Shows-Old\"\n611\t )\n612\t\n613\t# ━━━ Radarr ━━━\n614\t HOST1_RADARR_URL=\"http://localhost:7878\"\n615\t HOST1_RADARR_API_KEY=\"d43a3ec6cf1549edb4af0cc63f98b2a9\"\n616\t HOST1_TMDB_API_KEY=\"3dac5e2e49b5540472d2eafec4f01260\"\n617\t HOST1_RADARR_MOVIES_ROOT=\"/mnt/user/Movies\"\n618\t\n619\t# Note: stand-up_comedy in both Radarr + Sonarr — movie specials and TV specials, one folder\n620\t declare -A HOST1_RADARR_PATH_MAP=(\n621\t [\"/movies\"]=\"/mnt/user/Movies\"\n622\t [\"/kids movies\"]=\"/mnt/user/Kids_Movies\"\n623\t [\"/ext-stand-up-comedy\"]=\"/mnt/user/stand-up_comedy\"\n624\t [\"/anime-movies\"]=\"/mnt/user/Anime_Movies-Old\"\n625\t )\n626\t\n627\t# ━━━ Arr Recovery Toggles ━━━\n628\t# false = skip that arr on this host — exits cleanly without error\n629\t HOST1_SONARR_RECOVERY=true\n630\t HOST1_RADARR_RECOVERY=true\n631\t HOST1_LIDARR_RECOVERY=true # HOST1 only — exits cleanly on HOST2\n632\t\n633\t# ==============================================================================================\n634\t# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────\n635\t# ==============================================================================================\n636\t# Per-host check toggles and NIC config for system_watchdog.sh.\n637\t# Aliased by detect_hosts() — script uses unprefixed SYS_WATCHDOG_* names.\n638\t# HOST1: TR1950X 128GB — full media server, active transcoding, ZFS cache pools.\n639\t#\n640\t# Three-tier response — all critical checks enabled by default on HOST1:\n641\t# Tier 1 (bypass strikes, reboot now): docker daemon, rootfs full, kernel oops, FD, /boot\n642\t# Tier 2 (bypass strikes with OOM): RAM critical + OOM kills in cycle\n643\t# Tier 3 (standard strike system): everything else\n644\t#\n645\t# RAM tiers, OOM limits, and reboot loop settings in master.conf System Watchdog section.\n646\t\n647\t# ━━━ Primary NIC ━━━\n648\t# Network interface for NIC state check — verify with: ip link show | grep \"^[0-9]\"\n649\t# Common values: eth0, bond0, br0, eno1\n650\t HOST1_SYS_WATCHDOG_NIC=\"eth0\"\n651\t\n652\t# ━━━ Tier 1 — Critical Checks ━━━\n653\t# These bypass the strike system — a single hit triggers immediate reboot.\n654\t# Disabling any of these is not recommended — they protect against acute system failure.\n655\t\n656\t# Docker daemon unresponsive → try restart, reboot if restart fails.\n657\t# Without a working daemon docker_watchdog.sh is blind and containers cannot be managed.\n658\t HOST1_SYS_WATCHDOG_CHECK_DOCKER_DAEMON=true\n659\t\n660\t# rootfs at critical threshold (ROOTFS_CRITICAL_PCT=99) → reboot immediately.\n661\t# At 99% rootfs writes fail silently — logs stop, Docker errors out, SSH may stop working.\n662\t# Standard 95% threshold still uses strike system — only 99%+ is critical tier.\n663\t HOST1_SYS_WATCHDOG_CHECK_ROOTFS=true\n664\t\n665\t# Kernel BUG/Oops in dmesg delta since last cycle → reboot immediately.\n666\t# A kernel oops means the kernel ran with a corrupted state — stability is not guaranteed.\n667\t HOST1_SYS_WATCHDOG_CHECK_KERNEL_OOPS=true\n668\t\n669\t# File descriptor exhaustion at FD_CRITICAL_PCT (95%) → reboot immediately.\n670\t# At 95% FD: new connections fail, Docker can't spawn processes, SSH drops.\n671\t HOST1_SYS_WATCHDOG_CHECK_FD=true\n672\t\n673\t# /boot read-only detected → reboot immediately.\n674\t# Unexpected read-only /boot means state files and config writes are silently failing.\n675\t# Fallback state, watchdog reboot log, and lock files all go stale silently.\n676\t HOST1_SYS_WATCHDOG_CHECK_BOOT=true\n677\t\n678\t# ━━━ Tier 2 — Urgent OOM Check ━━━\n679\t# Bypass strikes when RAM is critically low AND OOM kill rate confirms active crisis.\n680\t# Both must be enabled for Tier 2 bypass to function — disable either to always use strikes.\n681\t\n682\t# Track kernel OOM kills each cycle via /proc/vmstat oom_kill delta.\n683\t# Also provides diagnostic context in reboot messages (which processes were killed).\n684\t HOST1_SYS_WATCHDOG_CHECK_OOM=true\n685\t\n686\t# Free RAM check — required for both Tier 2 bypass and RAM tier logic.\n687\t# Tiers: MEM_WARN_GB(10) → notify | MEM_SHUTDOWN_GB(6) → stop containers | MEM_GB(4) → strikes\n688\t HOST1_SYS_WATCHDOG_CHECK_RAM=true\n689\t\n690\t# ━━━ Tier 3 — Standard Checks (strike system) ━━━\n691\t# Each check must fail SYS_WATCHDOG_STRIKE_LIMIT consecutive cycles before action is taken.\n692\t# Single spikes are ignored — sustained problems trigger reboot.\n693\t\n694\t# /var/log filesystem usage above SYS_WATCHDOG_LOG_PCT.\n695\t# Log spam (Docker log storms, syslog loops) fills rootfs — indicates something broken.\n696\t HOST1_SYS_WATCHDOG_CHECK_LOG=true\n697\t\n698\t# ZFS ARC memory pinned above SYS_WATCHDOG_ARC_PINNED_PCT after cache drop.\n699\t# Enabled on HOST1 — ZFS cache pools actively used. Disable on hosts without ZFS.\n700\t HOST1_SYS_WATCHDOG_CHECK_ARC=true\n701\t\n702\t# CPU temperature above SYS_WATCHDOG_CPU_TEMP_MAX (95°C).\n703\t# Sustained high temp causes kernel throttling or panic. Requires lm-sensors.\n704\t HOST1_SYS_WATCHDOG_CHECK_CPU_TEMP=true\n705\t\n706\t# Load average above SYS_WATCHDOG_LOAD_MULTIPLIER × core count.\n707\t# DISABLED on HOST1 — Tdarr and Emby cause legitimate sustained load spikes during encoding.\n708\t# Enable on idle servers or adjust SYS_WATCHDOG_LOAD_MULTIPLIER if load is always high.\n709\t HOST1_SYS_WATCHDOG_CHECK_LOAD=false\n710\t\n711\t# Zombie process count above SYS_WATCHDOG_ZOMBIE_LIMIT (50).\n712\t# Large zombie counts indicate serious process management failure — something is stuck.\n713\t HOST1_SYS_WATCHDOG_CHECK_ZOMBIES=true\n714\t\n715\t# Check docker_watchdog.sh persistent skip list — required containers on skip list.\n716\t# Cross-watchdog coordination: if docker_watchdog gave up, system_watchdog escalates.\n717\t# ENABLED — HOST1 fully built and operational, skip list is meaningful.\n718\t HOST1_SYS_WATCHDOG_CHECK_CONTAINERS=true\n719\t\n720\t# /tmp filesystem usage above SYS_WATCHDOG_TMP_PCT with auto-clear attempt.\n721\t# Script tries to clear aged /tmp files first — only strikes if clear fails.\n722\t# Lock files, rsync temp files, and Docker ops use /tmp — 100% means lock failures.\n723\t HOST1_SYS_WATCHDOG_CHECK_TMP=true\n724\t\n725\t# Array disk error count delta in /proc/mdstat — accumulating errors = disk failing now.\n726\t# Triggers on SYS_WATCHDOG_MDSTAT_ERROR_LIMIT new errors in one cycle.\n727\t HOST1_SYS_WATCHDOG_CHECK_MDSTAT=true\n728\t\n729\t# Primary NIC operstate — detects NIC going down (physical or driver failure).\n730\t# Uses HOST1_SYS_WATCHDOG_NIC above. Strike system — brief flaps don't trigger reboot.\n731\t HOST1_SYS_WATCHDOG_CHECK_NETWORK=true\n732\t\n733\t# sshd running check — attempts restart before escalating.\n734\t# sshd down = no remote access. Script tries rc.sshd start, notifies, strikes on failure.\n735\t HOST1_SYS_WATCHDOG_CHECK_SSHD=true\n736\t\n737\t# Runaway process detection — single process above SYS_WATCHDOG_RUNAWAY_CPU_PCT sustained.\n738\t# DISABLED — Tdarr encoding and Emby transcoding legitimately peg CPU for extended periods.\n739\t# Enable only if HOST1 has no CPU-intensive workloads.\n740\t HOST1_SYS_WATCHDOG_CHECK_RUNAWAY=false\n741\t\n742\t# ==============================================================================================\n743\t# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────\n744\t# ==============================================================================================\n745\t# Containers to manage under pressure — see master.conf RW_CRITICAL_CONTAINERS for exclusions.\n746\t\n747\t# docker pause at medium pressure (RAM < RW_RAM_MEDIUM_GB or load > medium threshold)\n748\t# Suspended in-place — instant to pause/unpause, no state lost, no restart delay.\n749\t HOST1_RW_PAUSE_CONTAINERS=(\n750\t \"Huntarr\" # arr search automation — safe to suspend\n751\t \"Cleanuparr\" # download cleanup — safe to suspend\n752\t \"Healarr\" # arr health checks — safe to suspend\n753\t \"Soularr\" # Slskd automation — background only\n754\t \"ChannelTube\" # YouTube archiver — background only\n755\t \"Pinchflat\" # YouTube archiver — background only\n756\t )\n757\t\n758\t# docker stop at hard pressure (RAM < RW_RAM_HARD_GB)\n759\t# Full stop — these are optional/heavy services that free significant RAM when stopped.\n760\t# resource_watchdog.sh restarts them when pressure fully clears (RAM >= RW_RAM_RECOVER_GB).\n761\t HOST1_RW_STOP_CONTAINERS=(\n762\t \"LocalAI\" # GPU/CPU heavy — largest RAM consumer when idle\n763\t \"7DaysToDie\" # game server — optional\n764\t \"V-Rising\" # game server — optional\n765\t \"Code-Server\" # IDE — not needed during pressure events\n766\t )\n767\t\n768\t# ==============================================================================================\n769\t# ──────────────────────── End Of HOST1 Variables ──────────────────────────────────────────────\n770\t# ==============================================================================================\n771\t HOST1_UNRAID_API_KEY=\"1825c3a2e03ea5089974f4da2e171aa2d5907a1dea23cc479c33e492c8ff4dbb\"\n772\t"}]},"uuid":"af24d154-6a71-4623-ab99-df30552b1454","timestamp":"2026-06-06T17:48:52.495Z","toolUseResult":{"type":"text","file":{"filePath":"/boot/config/plugins/varaverk/Configurations/host1.conf","content":"#!/bin/bash\n# ==============================================================================================\n# ========================== HOST1 CONFIGURATION — unRAID-Gmer4Lfe ============================\n# ==============================================================================================\n# HOST1-specific variables — credentials, container names, share paths, failover lists.\n# Sourced after master.conf — values here extend shared profile arrays and add HOST1-specific\n# identity, credentials, and container configuration.\n#\n# Sparse checkout (git) ensures HOST2 never receives this file.\n# HOST2 never sees HOST1 credentials — clean separation at the file level.\n#\n# DO NOT put shared config here — thresholds, toggles, profiles belong in master.conf.\n# DO NOT put HOST2 variables here — they belong in host2.conf.\n#\n# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────\n#\n# ── IDENTITY & CONNECTIVITY ────────────────────────────────────────────────────────────────\n# IDENTITY hostname, SSH key\n# EMBY container name, URL, API key\n# NOTIFICATIONS Discord webhook\n# PARTNERSHIP auth containers, backup paths\n#\n# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────\n# DAILY SYNC SHARES media shares HOST1 owns and pushes to HOST2\n# WEEKLY SYNC SHARES appdata shares synced weekly (Sunday 2:30am)\n# CRITICAL SYNC SHARES appdata shares synced every 30 minutes\n# BACKUP VERIFY shares for checksum verification against remote\n# HOST1 RSYNC PROFILE host1-appdata profile for HOST1-specific appdata syncs\n#\n# ── DOCKER ─────────────────────────────────────────────────────────────────────────────────\n# DOCKER DAILY RESTART containers restarted daily\n# DOCKER WEEKLY RESTART containers restarted weekly\n# DOCKER WATCHDOG memory limits, health URLs, required containers, ignore list\n# DOCKER NETWORK CONNECT networks and containers for docker_network_connect.sh\n#\n# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────\n# DDNS DDNS containers managed by HOST1\n# INTERNET LOSS containers stopped when internet is lost\n# FALLBACK TIERS what HOST1 runs for HOST2 per tier\n# TIER DELAYS how long HOST1 must be down before each tier activates on HOST2\n# RSYNC WRITEBACK HOST1 appdata synced back on handback\n#\n# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────\n# MEDIA PERMISSIONS share list for media_shares_permissions.sh\n# MEDIA CLEANER folder lists for media_cleaner.sh\n#\n# ── MONITORS ───────────────────────────────────────────────────────────────────────────────\n# CERTIFICATE MONITOR domains checked for SSL expiry\n# SMART HEALTH drives to skip in SMART monitoring\n# ZFS REPORT pools to exclude from ZFS health report\n#\n# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────\n# TRANSCODES ramdisk size, thresholds, SSD path, server array\n#\n# ── ARR STACK ──────────────────────────────────────────────────────────────────────────────\n# DOWNLOADERS slskd, SABnzbd, qBittorrent credentials and URLs\n# LIDARR URL, API key, path map\n# SONARR URL, API key, path map\n# RADARR URL, API key, path map\n# ARR RECOVERY per-arr recovery toggles\n#\n# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────\n# SYSTEM WATCHDOG per-host check toggles and NIC configuration\n#\n# ── RESOURCE MANAGER ───────────────────────────────────────────────────────────────────────\n# RESOURCE MANAGER containers paused/stopped under memory pressure\n#\n# ==============================================================================================\n\n# ==============================================================================================\n# ── IDENTITY & CONNECTIVITY ───────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Identity ━━━\n# HOST1 hostname lives in master.conf (not a credential — safe for all servers).\n# SSH key used for all server-to-server operations — rsync, failover container commands.\n# Must be in /root/.ssh/ and authorised in HOST2's /root/.ssh/authorized_keys.\n HOST1_SSH_KEY=\"/root/.ssh/gmer4lfe_rsync_automation\"\n HOST1_OWNER=\"gmer4lfe\"\n HOST1_OWNER_EMAIL=\"gmer4lfe@gmail.com\"\n\n# ━━━ Emby ━━━\n# Referenced by transcode_manager.sh, emby_session_report.sh, emby_database_repair.sh,\n# weekly_sync_maintenance.sh, and HOST1_TRANSCODE_SERVERS below.\n# API key: Emby Dashboard → API Keys → + New Key\n HOST1_EMBY_CONTAINER=\"Emby\"\n HOST1_EMBY_URL=\"http://localhost:8096\"\n HOST1_EMBY_API_KEY=\"0c27448d93a7431f9ac63569f7655829\"\n\n# ━━━ Jellyfin ━━━\n# API key: Jellyfin Dashboard → Administration → API Keys → + New Key\n HOST1_JELLYFIN_CONTAINER=\"Jellyfin\"\n HOST1_JELLYFIN_URL=\"http://localhost:8095\"\n HOST1_JELLYFIN_API_KEY=\"4e820e7df74c4933acec212b1996314e\"\n\n# ━━━ Gitea ━━━\n# Personal access token for gitea_ssh_setup.sh — registers this server's SSH public key\n# with Gitea so git operations use key auth instead of passwords.\n# Create in Gitea: Settings → Applications → Generate Token → scope: write:user\n HOST1_GITEA_API_TOKEN=\"\"\n\n# ━━━ Notifications ━━━\n# Discord webhook — leave blank to disable.\n# Per-host so HOST1 and HOST2 can post to different channels or only one server notifies.\n HOST1_DISCORD_WEBHOOK=\"\"\n\n# ━━━ Partnership ━━━\n# HOST1 is always the owner (source of truth) unless --transfer has been run.\n# See README-Partnership.md and master.conf PARTNERSHIP section for full lifecycle docs.\n\n# Auth containers reconfigured on onboard/offboard.\n# Format: \"ContainerName|WebUIPort\"\n# On onboard → WebUI pointed at owner's Tailscale IP (mirror clicks NPM, gets owner's auth)\n# On offboard → WebUI pointed back at localhost\n HOST1_PARTNERSHIP_AUTH_WEBUIS=(\n \"NginxProxyManager|81\"\n \"Lldap-Gmer4Lfe|17170\"\n \"Authelia|9091\"\n \"Authelia-Secondary|9092\"\n )\n\n# XML templates (from this server's templates-user/) pushed to mirror during onboard.\n# These become the mirror's active auth stack, backed by the rsync-synced appdata.\n# Update filename if Lldap is renamed to drop the host suffix.\n HOST1_PARTNERSHIP_AUTH_STACK=(\n # Dependencies first — Mariadb/Redis must be healthy before Authelia starts\n \"my-Mariadb-Authelia.xml\"\n \"my-Mariadb-Authelia-Secondary.xml\"\n \"my-Redis-Authelia.xml\"\n \"my-Redis-Authelia-Secondary.xml\"\n # Auth apps — deployed after their deps are confirmed healthy\n \"my-Authelia.xml\"\n \"my-Authelia-Secondary.xml\"\n \"my-NginxProxyManager.xml\"\n \"my-Lldap-Gmer4Lfe.xml\"\n # Source of truth — must be available on HOST2 independently of the auth stack\n \"my-Gitea.xml\"\n )\n\n# XML templates pushed to mirror for the arr stack during onboard.\n# Deps (e.g. databases) first if any — same ordering rule as auth stack.\n HOST1_PARTNERSHIP_ARR_STACK=(\n # \"my-Sonarr.xml\"\n # \"my-Radarr.xml\"\n # \"my-Lidarr.xml\"\n # \"my-Prowlarr.xml\"\n # \"my-Bazarr.xml\"\n )\n\n# Paths HOST2 should collect during the grace window after offboard.\n# Notified on offboard — no auto-deletion, HOST2 must collect manually within PARTNERSHIP_GRACE_HOURS.\n HOST1_PARTNERSHIP_MIRROR_BACKUPS=(\n # \"/mnt/user/appdata-Fallback/Jayred365-Emby\"\n )\n\n# Containers parked on this server when partnership is active.\n# Stopped on onboard (owner deploys its stack instead), restarted on offboard.\n HOST1_PARTNERSHIP_OWN_CONTAINERS=(\n # \"Emby\"\n # \"NginxProxyManager\"\n )\n\n# Emby admin provisioning — toggle is owner-only, credentials are per-host.\n# Owner enables/disables the feature. Each host sets the account they want on the shared Emby.\n# On onboard: owner reads mirror's HOST*_PARTNERSHIP_EMBY_ADMIN_* and creates that account.\n# On offboard: account is deleted. Username collision → onboard exits with error.\n HOST1_PARTNERSHIP_PROVISION_EMBY_ADMIN=false # owner controls whether Emby is shared\n HOST1_PARTNERSHIP_EMBY_PORT=8096\n HOST1_PARTNERSHIP_EMBY_ADMIN_USER=\"\" # this server's desired Emby username\n HOST1_PARTNERSHIP_EMBY_ADMIN_PASS=\"\" # this server's desired Emby password\n\n# ==============================================================================================\n# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Daily Sync Shares ━━━\n# Shares HOST1 pushes to all other nodes every night (1am via daily_sync_maintenance.sh).\n# Mesh model: every node pushes every media share — no ownership, no mirrors.\n# arr_sync ensures all arr libraries converge (union). rsync spreads files (additive, no --delete).\n# arr_cleanup removes true orphans based on local arr state.\n# Any node can download content to any share — it propagates to all nodes on the next cycle.\n# Nextcloud is intentionally one-directional (HOST1→HOST2 offsite backup — not arr-managed).\n# Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed.\n# For shares needing container stops or custom options — add a profile in master.conf.\n HOST1_DAILY_SYNC_SHARES=(\n /mnt/user/Books\n /mnt/user/Intros\n /mnt/user/Kids_Movies\n /mnt/user/Kids_Tv_Shows\n /mnt/user/Movies\n /mnt/user/Music\n /mnt/user/Music_Videos\n /mnt/user/Nextcloud\n /mnt/user/stand-up_comedy\n /mnt/user/Sports\n /mnt/user/Tv_Shows\n /mnt/user/Anime_Shows-Old\n /mnt/user/Anime_Movies-Old\n /mnt/user/Anime_Movies\n /mnt/user/Anime_Shows\n )\n\n# Personal encrypted shares — synced for offsite backup, independent of media shares.\n# ZFS encrypted at dataset level — remote receives encrypted blocks, cannot read content.\n# See README-Rsync_Setup.md for ZFS encryption setup before uncommenting.\n HOST1_PERSONAL_SHARES=(\n # /mnt/user/HOST1-Personal # uncomment after creating encrypted dataset\n )\n\n# ━━━ Weekly Sync Shares ━━━\n# Appdata shares synced during the weekly maintenance window (Sunday 2:30am).\n# Containers stopped both sides before sync — full clean state guaranteed.\n# Profiles drive container stops, excludes, and options — configured in master.conf RSYNC section.\n# Order matters — Emby first (larger transfer), then Critical-Data (auth stack).\n HOST1_WEEKLY_SYNC_SHARES=(\n \"/mnt/user/Media_Server/Emby\" # emby profile — full clean mirror\n \"/mnt/user/appdata-Fallback/Critical-Data\" # critical-data profile — auth stack\n )\n\n# ━━━ Intermediate Sync Shares ━━━\n# Shares synced every 4 hours by intermediate_sync_maintenance.sh.\n# Uses DEFAULT_RSYNC_OPTS (no --delete) — for sub-daily propagation of metadata or watch state.\n# Full media share sync stays in the daily window. Leave empty to skip mid-day rsync entirely.\n HOST1_INTERMEDIATE_SYNC_SHARES=(\n # Add shares here to enable mid-day rsync\n # Example: \"/mnt/user/Emby_Metadata\"\n )\n\n# ━━━ Critical Sync Shares ━━━\n# Appdata shares synced every 30 minutes by critical_sync_maintenance.sh.\n# Format: \"/path/to/share\" or \"/path/to/share|profile-name\"\n# Order matters — Critical-Data first (auth stack), then Emby dirty sync.\n HOST1_CRITICAL_SYNC_SHARES=(\n \"/mnt/user/appdata-Fallback/Critical-Data|critical-fallback\" # auth dirty sync — stays running\n \"/mnt/user/Media_Server/Emby|emby-fallback\" # Emby dirty sync — stays running\n )\n\n# ━━━ Backup Verify ━━━\n# Shares verified by backup_verify.sh — random file checksum comparison against remote.\n# Leave empty to use HOST1_DAILY_SYNC_SHARES automatically.\n# Sample size and minimum file size defined in master.conf.\n HOST1_BACKUP_VERIFY_SHARES=(\n # leave empty to use HOST1_DAILY_SYNC_SHARES automatically\n )\n\n# ━━━ HOST1 Rsync Profile — host1-appdata ━━━\n# HOST1-specific appdata sync profile — extends the shared PROFILE_* arrays in master.conf.\n# Use for appdata unique to HOST1 (Organizrv2, VaultWarden, UptimeKuma etc.)\n# Shared appdata (auth stack, Emby) use dedicated profiles defined in master.conf.\n# Run manually: bash Rsync/rsync.sh /mnt/user/appdata-Fallback/HOST1-Appdata --profile=host1-appdata\n PROFILE_RSYNC_OPTS[host1-appdata]=\"-av --info=progress2 --bwlimit=${PROFILE_BW_LIMIT[host1-appdata]:-8000}\"\n PROFILE_BW_LIMIT[host1-appdata]=8000\n PROFILE_RETRY_COUNT[host1-appdata]=3\n PROFILE_SLEEP[host1-appdata]=300\n PROFILE_CRITICAL_CONTAINER_NAMES[host1-appdata]=\"Organizrv2-Gmer4Lfe UptimeKuma-Gmer4Lfe VaultWarden-Gmer4Lfe\"\n PROFILE_DELAYED_CONTAINERS[host1-appdata]=\"\"\n PROFILE_CONTAINER_DELAY[host1-appdata]=5\n PROFILE_EXCLUDE_DIRS[host1-appdata]=\"logs *.tmp\"\n\n# ==============================================================================================\n# ── DOCKER ────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Docker Daily Restart ━━━\n# Containers restarted every day via DAILY_MAINTENANCE_SCRIPTS.\n# Dispatcharr degrades over time without restart — daily is intentional, not just housekeeping.\n# Order matters — auth stack first, then media services.\n HOST1_DAILY_RESTART_CONTAINERS=(\n \"NginxProxyManager\"\n \"Lldap-Gmer4Lfe\"\n \"Authelia\"\n \"Authelia-Secondary\"\n \"Dispatcharr-Iptv-Users\"\n \"Dispatcharr\" # Live TV scheduler — degrades without daily restart\n \"Dispatcharr-Basic\"\n \"ErsatzTV-Emby\"\n \"Slskd\" # Soulseek connection drops after extended uptime; restart refreshes share index\n )\n\n# ━━━ Docker Weekly Restart ━━━\n# Less critical services restarted weekly via WEEKLY_MAINTENANCE_SCRIPTS (Sunday 2:30am).\n# Containers already stopped for weekly sync — restart adds zero extra downtime.\n HOST1_WEEKLY_RESTART_CONTAINERS=(\n \"NextCloud\"\n \"Organizrv2-Gmer4Lfe\"\n \"AdGuard-Home\"\n \"Immich-Gmer4Lfe\"\n )\n\n# ━━━ Docker Watchdog ━━━\n# Per-HOST1 container configuration for docker_watchdog.sh.\n# Shared thresholds and toggles live in master.conf.\n\n# Memory hard limits in MB — immediate restart if exceeded.\n# Set at \"container is clearly broken\" not \"container is busy\".\n# 20GB=20480 18GB=18432 16GB=16384 12GB=12288 8GB=8192 4GB=4096 2GB=2048 1GB=1024\n declare -A HOST1_WATCHDOG_CONTAINERS=(\n [\"Emby\"]=20480 # 20GB — large library + active transcodes\n [\"LidaTube\"]=6144 # 6GB — memory leak over time\n [\"Tdarr\"]=6144 # 6GB — encoding is memory intensive\n [\"Code-Server\"]=1024 # 1GB — should never need more\n )\n\n# HTTP health check URLs — checked every cycle, strike system before restart.\n# Only add containers with a meaningful web interface to check.\n declare -A HOST1_WATCHDOG_CONTAINER_URLS=(\n [\"Emby\"]=\"http://localhost:8096\"\n )\n\n# Required containers — must always be running on HOST1.\n# Strike system before restart — repeated failures go on skip list, auto-clears on recovery.\n# Listed in dependency order — dependencies before dependents.\n HOST1_WATCHDOG_REQUIRED_CONTAINERS=(\n \"NginxProxyManager\"\n \"Lldap-Gmer4Lfe\"\n \"Mariadb-Authelia\"\n \"Mariadb-Authelia-Secondary\"\n \"Redis-Authelia\"\n \"Redis-Authelia-Secondary\"\n \"Authelia\"\n \"Authelia-Secondary\"\n )\n\n# Containers to skip in Tier 2 global scan — legitimately stopped or frequently restarting.\n# Watchdog leaves these alone entirely — no restart attempts, no crash loop tracking.\n HOST1_WATCHDOG_SCAN_IGNORE=(\n \"DashGate\"\n \"PIA-WG-Config-Generator\"\n \"Aperture\"\n \"Aperture-Kids\"\n \"pgvector-18-Apeture-Kids\"\n \"Pgvector18-Aperture\"\n \"emby-test\" # broken test container (exit 127 — bad image)\n )\n\n# Dependency ordering — skip restarting a container if its dependency is also down.\n# Prevents watchdog from restarting Authelia before Mariadb is back up.\n# SPACE-SEPARATED STRINGS — converted to array at runtime.\n declare -A HOST1_WATCHDOG_DEPENDENCIES=(\n [\"Authelia\"]=\"Mariadb-Authelia Redis-Authelia\"\n [\"Authelia-Secondary\"]=\"Mariadb-Authelia Redis-Authelia-Secondary\"\n [\"NextCloud\"]=\"Postgres-NextCloud\"\n )\n\n# Per-container appdata growth suppress ceilings in MB.\n# ONLY needed in specific cases — growth rate detection covers all containers automatically.\n# Use this when a container legitimately has large stable data and you want to guarantee\n# it never triggers a false-positive growth alert. Growth warnings are suppressed while the\n# container's dir stays below this ceiling; above it, warnings resume as normal.\n# 50GB=51200 25GB=25600 20GB=20480 15GB=15360 10GB=10240 5GB=5120\n declare -A HOST1_WATCHDOG_APPDATA_SIZES=(\n [\"Tdarr\"]=\"25600\" # 25GB — transcode cache grows legitimately during active jobs\n [\"7dtd\"]=\"20480\" # 20GB — game server world data, expected to be large\n )\n\n# ━━━ Network Watchdog ━━━\n# Host-specific connectivity config for Watchdogs/System/network_watchdog.sh.\n HOST1_NETWORK_WATCHDOG_DDNS_DOMAIN=\"gmer4lfe.com\"\n HOST1_NETWORK_WATCHDOG_DDNS_CONTAINER=\"Gmer4Lfe.com\"\n HOST1_NETWORK_WATCHDOG_NPM_URL=\"https://gmer4lfe.com\"\n\n# ━━━ Docker Network Connect ━━━\n# Containers connected to custom networks at array start by docker_network_connect.sh.\n# Networks created if they don't exist — idempotent, safe to re-run.\n HOST1_NETWORK_CONNECT_CONTAINERS=(\n \"memcached\"\n \"Npm-CrowdSec\"\n )\n\n HOST1_NETWORK_CONNECT_NETWORKS=(\n \"high-availability\"\n )\n\n# ==============================================================================================\n# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ DDNS ━━━\n# DDNS containers HOST1 manages — started/stopped by fallback.sh per DDNS absolute rules:\n# Internet loss → stop immediately\n# Failover → HOST2 starts HOST1's DDNS as Tier 1 (before any other containers)\n# Handback → stop HOST1's DDNS on HOST2 → rsync → start containers → start local DDNS last\n HOST1_DDNS_CONTAINERS=(\n \"Gmer4Lfe.com\"\n )\n\n# ━━━ Internet Loss ━━━\n# Containers stopped immediately on HOST1 when internet connection is lost.\n# Prevents external-facing services from operating without connectivity.\n FALLBACK_HOST1_STOP_ON_NO_NET=(\n \"Gmer4Lfe.com\"\n )\n\n# ━━━ Fallback Tiers — HOST1 Runs for HOST2 ━━━\n# Containers HOST1 starts when HOST2 goes down.\n# Tier 1 is always immediate — vital services cannot wait.\n# Higher tiers activate after HOST2_TIER*_DELAY minutes (set in host2.conf).\n FALLBACK_HOST1_COVERS_HOST2_TIER1=(\n \"Gmer4Lfe.us\"\n \"VaultWarden-Jayred365\"\n )\n\n FALLBACK_HOST1_COVERS_HOST2_TIER2=(\n # \"container-placeholder\"\n )\n\n FALLBACK_HOST1_COVERS_HOST2_TIER3=(\n # \"container-placeholder\"\n )\n\n FALLBACK_HOST1_COVERS_HOST2_TIER4=(\n # \"container-placeholder\"\n )\n\n# ━━━ Tier Delays — HOST1's Containers on HOST2 ━━━\n# How long HOST1 must be down before each tier activates on HOST2 — in minutes.\n# Tier 1 is always immediate — no delay var needed.\n HOST1_TIER2_DELAY=240 # 4 hours — NextCloud, Immich\n HOST1_TIER3_DELAY=720 # 12 hours — secondary services\n HOST1_TIER4_DELAY=1440 # 24 hours — arrs + downloaders\n\n# ━━━ Rsync Writeback — HOST1 Appdata Back on Handback ━━━\n# Syncs HOST1 appdata BACK to HOST1 when it comes back online after a failover.\n# Containers stopped before writeback — clean source, no competing writes.\n#\n# HOST1_TIER1_WRITEBACK_DELAY: short outages skip Tier 1 writeback — primary state\n# is more reliable than dirty sync data for brief outages.\n HOST1_TIER1_WRITEBACK_DELAY=60 # skip Emby writeback if outage under 1hr\n\n# Tier 4 automatically syncs HOST1_DAILY_SYNC_SHARES — only list paths NOT in that array.\n FALLBACK_HOST1_WRITEBACK_TIER1=(\n \"/mnt/user/Media_Server/Emby\" # watch states built up during outage\n )\n\n FALLBACK_HOST1_WRITEBACK_TIER2=(\n \"/mnt/user/appdata-Fallback/Important-Data\" # NextCloud + Postgres — files added during outage\n )\n\n FALLBACK_HOST1_WRITEBACK_TIER3=(\n # \"location-placeholder\"\n )\n\n FALLBACK_HOST1_WRITEBACK_TIER4=(\n \"/mnt/user/appdata-Fallback/Arrs_Stack\" # arr databases — downloads queued during outage\n )\n\n# ==============================================================================================\n# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Media Permissions ━━━\n# Shares that media_shares_permissions.sh applies PERMISSIONS_MODE and PERMISSIONS_OWNER to.\n# Runs first in DAILY_MAINTENANCE_SCRIPTS — arr cleanup depends on correct ownership.\n HOST1_MEDIA_PERMISSION_SHARES=(\n /mnt/user/Anime_Movies\n /mnt/user/Anime_Movies-Old\n /mnt/user/Anime_Shows\n /mnt/user/Anime_Shows-Old\n /mnt/user/appcache\n /mnt/user/Books\n /mnt/user/Downloads\n /mnt/user/Games\n /mnt/user/Intros\n /mnt/user/Kids_Movies\n /mnt/user/Kids_Tv_Shows\n /mnt/user/Movie_Recordings\n /mnt/user/Movies\n /mnt/user/Music\n /mnt/user/Music_Videos\n /mnt/user/Photo\n /mnt/user/Sports\n /mnt/user/stand-up_comedy\n /mnt/user/Temp_Storage\n /mnt/user/Tv_Recordings\n /mnt/user/Tv_Shows\n /mnt/user/YouTube\n )\n\n# ━━━ Media Cleaner ━━━\n# Folder lists for media_cleaner.sh — two profiles: anime and media.\n# File patterns shared across all servers — defined in master.conf.\n# Called via DAILY_MAINTENANCE_SCRIPTS. Run manually: Media/media_cleaner.sh anime|media\n HOST1_ANIME_CLEAN_FOLDERS=(\n /mnt/user/Anime_Movies\n /mnt/user/Anime_Movies-Old\n /mnt/user/Anime_Shows\n /mnt/user/Anime_Shows-Old\n )\n\n HOST1_MEDIA_CLEAN_FOLDERS=(\n /mnt/user/Kids_Movies\n /mnt/user/Kids_Tv_Shows\n /mnt/user/Movies\n /mnt/user/Music\n /mnt/user/Sports\n /mnt/user/stand-up_comedy\n /mnt/user/Tv_Shows\n )\n\n# ==============================================================================================\n# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Certificate Monitor ━━━\n# Domains checked via direct openssl connection — not relying on NPM's certificate state.\n# Checks the actual certificate served, not what NPM thinks it has.\n# Thresholds (CERT_WARN_DAYS, CERT_CRIT_DAYS) defined in master.conf.\n HOST1_CERT_MONITOR_DOMAINS=(\n \"Gmer4Lfe.com\"\n \"Gmer4Lfe.us\"\n )\n\n# ━━━ SMART Health ━━━\n# Drives skipped in SMART attribute monitoring — hardware is server-specific.\n# Thresholds read from dynamix.cfg at runtime — fallbacks in master.conf.\n HOST1_SMART_IGNORE_DRIVES=(\n \"sda\" # boot USB — SMART not meaningful on flash drives\n )\n\n# ━━━ ZFS Report ━━━\n# Pools excluded from the weekly ZFS health report — reduces noise from single-disk array pools.\n# These are individual array disks formatted as ZFS — converting to XFS over time via unBalance.\n# Pool health thresholds defined in master.conf.\n HOST1_ZFS_REPORT_IGNORE_POOLS=(\n \"disk5\"\n \"disk6\"\n \"disk8\"\n \"disk9\"\n \"disk10\"\n )\n\n# ==============================================================================================\n# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# Ramdisk size ceiling — tmpfs only uses RAM actually needed, not the full size upfront.\n# Real-world: 9 streams peaked at ~5.5GB — 10G gives generous headroom on 128GB RAM.\n HOST1_RAMDISK_SIZE=\"10G\"\n\n# Usage thresholds — coupled to HOST1_RAMDISK_SIZE, adjust all three together if size changes.\n# Hysteresis gap (8.5 - 7 = 1.5GB) prevents flip-flop between ramdisk and SSD.\n HOST1_RAMDISK_WARN_GB=8.5 # flip to SSD when ramdisk usage reaches this\n HOST1_RAMDISK_LOW_GB=7 # flip back to ramdisk when usage drops to this\n\n# SSD fallback path — where transcodes land when ramdisk exceeds HOST1_RAMDISK_WARN_GB.\n# Must be on cache pool — array disks too slow for active transcode writes.\n HOST1_TRANSCODE_SSD=\"/mnt/cache/Temp_Storage/Emby/Transcodes/\"\n\n# Media servers sharing the ramdisk transcode space on HOST1.\n# Format: \"ContainerName|URL|APIKey|Type\" — Type: emby | jellyfin | plex\n# Entries with placeholder API keys are skipped automatically.\n# ⚠️ Tdarr does NOT belong here — keep Tdarr on SSD, not ramdisk.\n HOST1_TRANSCODE_SERVERS=(\n \"${HOST1_EMBY_CONTAINER}|${HOST1_EMBY_URL}|${HOST1_EMBY_API_KEY}|emby\"\n \"${HOST1_JELLYFIN_CONTAINER}|${HOST1_JELLYFIN_URL}|${HOST1_JELLYFIN_API_KEY}|jellyfin\"\n )\n\n# ==============================================================================================\n# ── ARR STACK ─────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Used by arr cleanup scripts and arrs_failed_stalled_recovery.sh.\n# detect_hosts() selects HOST1 vars when running on HOST1.\n#\n# PATH MAPS — container path → host path translation.\n# Arr stores file paths using container-internal paths — scripts need host paths to scan.\n# Add one entry per root folder in arr Settings → Media Management → Root Folders.\n\n# ━━━ Downloaders ━━━\n# Used by downloaders_reset.sh — runs every 30min via CRITICAL_MAINTENANCE_SCRIPTS.\n# Clears stuck states, purges old history, prepares each client for a clean cycle.\n\n# slskd — clears stuck searches, dead transfers, purges expired failed imports.\n# SLSKD_FAILED_IMPORTS_DIR: where Soularr moves albums Lidarr rejected.\n HOST1_SLSKD_URL=\"http://localhost:8980\"\n HOST1_SLSKD_API_KEY=\"4bF9kL2mNpQrT7vWxYz1A3dEgHjKoRsU\"\n HOST1_SLSKD_FAILED_IMPORTS_DIR=\"/mnt/user/Temp_Storage/Slskd/completed/failed_imports\"\n\n# SABnzbd\n HOST1_SABNZBD_URL=\"http://localhost:8180\"\n HOST1_SABNZBD_API_KEY=\"8bfefe41d83b4d50883e32859b55ca9a\"\n\n# qBittorrent — deleteFiles=false removes torrent from qBit but leaves files on disk.\n# Radarr/Sonarr manage actual files independently.\n HOST1_QBIT_URL=\"http://localhost:8080\"\n HOST1_QBIT_USERNAME=\"root\"\n HOST1_QBIT_PASSWORD=\"Stay0utD!ck\"\n\n# ━━━ Lidarr — HOST1 only ━━━\n# HOST2 does not run Lidarr — HOST1_LIDARR_RECOVERY flag handles the exit cleanly.\n HOST1_LIDARR_URL=\"http://localhost:8686\"\n HOST1_LIDARR_API_KEY=\"b2977e71ef074bc0a0529d9fcce3b2dc\"\n HOST1_LIDARR_MUSIC_ROOT=\"/mnt/user/Music-New\"\n HOST1_FANART_API_KEY=\"Yd7147a43b692df0b364b94dc47efb81\"\n HOST1_LASTFM_API_KEY=\"be6dc169c33ae263e690c30d18b7491d\"\n\n declare -A HOST1_LIDARR_PATH_MAP=(\n [\"/ext-music\"]=\"/mnt/user/Music-New\"\n )\n\n# ━━━ Sonarr ━━━\n HOST1_SONARR_URL=\"http://localhost:8989\"\n HOST1_SONARR_API_KEY=\"130decd3db5b4c25afad64864cd03f9f\"\n HOST1_SONARR_TV_ROOT=\"/mnt/user/Tv_Shows\"\n\n# Note: stand-up_comedy in both Sonarr + Radarr — TV specials and movie specials, one folder\n declare -A HOST1_SONARR_PATH_MAP=(\n [\"/tv\"]=\"/mnt/user/Tv_Shows\"\n [\"/ext-standup-comedy\"]=\"/mnt/user/stand-up_comedy\"\n [\"/kids tv\"]=\"/mnt/user/Kids_Tv_Shows\"\n [\"/ext-anime-shows\"]=\"/mnt/user/Anime_Shows-Old\"\n )\n\n# ━━━ Radarr ━━━\n HOST1_RADARR_URL=\"http://localhost:7878\"\n HOST1_RADARR_API_KEY=\"d43a3ec6cf1549edb4af0cc63f98b2a9\"\n HOST1_TMDB_API_KEY=\"3dac5e2e49b5540472d2eafec4f01260\"\n HOST1_RADARR_MOVIES_ROOT=\"/mnt/user/Movies\"\n\n# Note: stand-up_comedy in both Radarr + Sonarr — movie specials and TV specials, one folder\n declare -A HOST1_RADARR_PATH_MAP=(\n [\"/movies\"]=\"/mnt/user/Movies\"\n [\"/kids movies\"]=\"/mnt/user/Kids_Movies\"\n [\"/ext-stand-up-comedy\"]=\"/mnt/user/stand-up_comedy\"\n [\"/anime-movies\"]=\"/mnt/user/Anime_Movies-Old\"\n )\n\n# ━━━ Arr Recovery Toggles ━━━\n# false = skip that arr on this host — exits cleanly without error\n HOST1_SONARR_RECOVERY=true\n HOST1_RADARR_RECOVERY=true\n HOST1_LIDARR_RECOVERY=true # HOST1 only — exits cleanly on HOST2\n\n# ==============================================================================================\n# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Per-host check toggles and NIC config for system_watchdog.sh.\n# Aliased by detect_hosts() — script uses unprefixed SYS_WATCHDOG_* names.\n# HOST1: TR1950X 128GB — full media server, active transcoding, ZFS cache pools.\n#\n# Three-tier response — all critical checks enabled by default on HOST1:\n# Tier 1 (bypass strikes, reboot now): docker daemon, rootfs full, kernel oops, FD, /boot\n# Tier 2 (bypass strikes with OOM): RAM critical + OOM kills in cycle\n# Tier 3 (standard strike system): everything else\n#\n# RAM tiers, OOM limits, and reboot loop settings in master.conf System Watchdog section.\n\n# ━━━ Primary NIC ━━━\n# Network interface for NIC state check — verify with: ip link show | grep \"^[0-9]\"\n# Common values: eth0, bond0, br0, eno1\n HOST1_SYS_WATCHDOG_NIC=\"eth0\"\n\n# ━━━ Tier 1 — Critical Checks ━━━\n# These bypass the strike system — a single hit triggers immediate reboot.\n# Disabling any of these is not recommended — they protect against acute system failure.\n\n# Docker daemon unresponsive → try restart, reboot if restart fails.\n# Without a working daemon docker_watchdog.sh is blind and containers cannot be managed.\n HOST1_SYS_WATCHDOG_CHECK_DOCKER_DAEMON=true\n\n# rootfs at critical threshold (ROOTFS_CRITICAL_PCT=99) → reboot immediately.\n# At 99% rootfs writes fail silently — logs stop, Docker errors out, SSH may stop working.\n# Standard 95% threshold still uses strike system — only 99%+ is critical tier.\n HOST1_SYS_WATCHDOG_CHECK_ROOTFS=true\n\n# Kernel BUG/Oops in dmesg delta since last cycle → reboot immediately.\n# A kernel oops means the kernel ran with a corrupted state — stability is not guaranteed.\n HOST1_SYS_WATCHDOG_CHECK_KERNEL_OOPS=true\n\n# File descriptor exhaustion at FD_CRITICAL_PCT (95%) → reboot immediately.\n# At 95% FD: new connections fail, Docker can't spawn processes, SSH drops.\n HOST1_SYS_WATCHDOG_CHECK_FD=true\n\n# /boot read-only detected → reboot immediately.\n# Unexpected read-only /boot means state files and config writes are silently failing.\n# Fallback state, watchdog reboot log, and lock files all go stale silently.\n HOST1_SYS_WATCHDOG_CHECK_BOOT=true\n\n# ━━━ Tier 2 — Urgent OOM Check ━━━\n# Bypass strikes when RAM is critically low AND OOM kill rate confirms active crisis.\n# Both must be enabled for Tier 2 bypass to function — disable either to always use strikes.\n\n# Track kernel OOM kills each cycle via /proc/vmstat oom_kill delta.\n# Also provides diagnostic context in reboot messages (which processes were killed).\n HOST1_SYS_WATCHDOG_CHECK_OOM=true\n\n# Free RAM check — required for both Tier 2 bypass and RAM tier logic.\n# Tiers: MEM_WARN_GB(10) → notify | MEM_SHUTDOWN_GB(6) → stop containers | MEM_GB(4) → strikes\n HOST1_SYS_WATCHDOG_CHECK_RAM=true\n\n# ━━━ Tier 3 — Standard Checks (strike system) ━━━\n# Each check must fail SYS_WATCHDOG_STRIKE_LIMIT consecutive cycles before action is taken.\n# Single spikes are ignored — sustained problems trigger reboot.\n\n# /var/log filesystem usage above SYS_WATCHDOG_LOG_PCT.\n# Log spam (Docker log storms, syslog loops) fills rootfs — indicates something broken.\n HOST1_SYS_WATCHDOG_CHECK_LOG=true\n\n# ZFS ARC memory pinned above SYS_WATCHDOG_ARC_PINNED_PCT after cache drop.\n# Enabled on HOST1 — ZFS cache pools actively used. Disable on hosts without ZFS.\n HOST1_SYS_WATCHDOG_CHECK_ARC=true\n\n# CPU temperature above SYS_WATCHDOG_CPU_TEMP_MAX (95°C).\n# Sustained high temp causes kernel throttling or panic. Requires lm-sensors.\n HOST1_SYS_WATCHDOG_CHECK_CPU_TEMP=true\n\n# Load average above SYS_WATCHDOG_LOAD_MULTIPLIER × core count.\n# DISABLED on HOST1 — Tdarr and Emby cause legitimate sustained load spikes during encoding.\n# Enable on idle servers or adjust SYS_WATCHDOG_LOAD_MULTIPLIER if load is always high.\n HOST1_SYS_WATCHDOG_CHECK_LOAD=false\n\n# Zombie process count above SYS_WATCHDOG_ZOMBIE_LIMIT (50).\n# Large zombie counts indicate serious process management failure — something is stuck.\n HOST1_SYS_WATCHDOG_CHECK_ZOMBIES=true\n\n# Check docker_watchdog.sh persistent skip list — required containers on skip list.\n# Cross-watchdog coordination: if docker_watchdog gave up, system_watchdog escalates.\n# ENABLED — HOST1 fully built and operational, skip list is meaningful.\n HOST1_SYS_WATCHDOG_CHECK_CONTAINERS=true\n\n# /tmp filesystem usage above SYS_WATCHDOG_TMP_PCT with auto-clear attempt.\n# Script tries to clear aged /tmp files first — only strikes if clear fails.\n# Lock files, rsync temp files, and Docker ops use /tmp — 100% means lock failures.\n HOST1_SYS_WATCHDOG_CHECK_TMP=true\n\n# Array disk error count delta in /proc/mdstat — accumulating errors = disk failing now.\n# Triggers on SYS_WATCHDOG_MDSTAT_ERROR_LIMIT new errors in one cycle.\n HOST1_SYS_WATCHDOG_CHECK_MDSTAT=true\n\n# Primary NIC operstate — detects NIC going down (physical or driver failure).\n# Uses HOST1_SYS_WATCHDOG_NIC above. Strike system — brief flaps don't trigger reboot.\n HOST1_SYS_WATCHDOG_CHECK_NETWORK=true\n\n# sshd running check — attempts restart before escalating.\n# sshd down = no remote access. Script tries rc.sshd start, notifies, strikes on failure.\n HOST1_SYS_WATCHDOG_CHECK_SSHD=true\n\n# Runaway process detection — single process above SYS_WATCHDOG_RUNAWAY_CPU_PCT sustained.\n# DISABLED — Tdarr encoding and Emby transcoding legitimately peg CPU for extended periods.\n# Enable only if HOST1 has no CPU-intensive workloads.\n HOST1_SYS_WATCHDOG_CHECK_RUNAWAY=false\n\n# ==============================================================================================\n# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Containers to manage under pressure — see master.conf RW_CRITICAL_CONTAINERS for exclusions.\n\n# docker pause at medium pressure (RAM < RW_RAM_MEDIUM_GB or load > medium threshold)\n# Suspended in-place — instant to pause/unpause, no state lost, no restart delay.\n HOST1_RW_PAUSE_CONTAINERS=(\n \"Huntarr\" # arr search automation — safe to suspend\n \"Cleanuparr\" # download cleanup — safe to suspend\n \"Healarr\" # arr health checks — safe to suspend\n \"Soularr\" # Slskd automation — background only\n \"ChannelTube\" # YouTube archiver — background only\n \"Pinchflat\" # YouTube archiver — background only\n )\n\n# docker stop at hard pressure (RAM < RW_RAM_HARD_GB)\n# Full stop — these are optional/heavy services that free significant RAM when stopped.\n# resource_watchdog.sh restarts them when pressure fully clears (RAM >= RW_RAM_RECOVER_GB).\n HOST1_RW_STOP_CONTAINERS=(\n \"LocalAI\" # GPU/CPU heavy — largest RAM consumer when idle\n \"7DaysToDie\" # game server — optional\n \"V-Rising\" # game server — optional\n \"Code-Server\" # IDE — not needed during pressure events\n )\n\n# ==============================================================================================\n# ──────────────────────── End Of HOST1 Variables ──────────────────────────────────────────────\n# ==============================================================================================\n HOST1_UNRAID_API_KEY=\"1825c3a2e03ea5089974f4da2e171aa2d5907a1dea23cc479c33e492c8ff4dbb\"\n","numLines":772,"startLine":1,"totalLines":772}},"sourceToolAssistantUUID":"0c5f94b8-15d6-40c2-be8b-ffcc6daf8cc1","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"af24d154-6a71-4623-ab99-df30552b1454","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_0158ToqvAAudKdZNWhTNLoMn","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01JjUWsFnTZi8dnuZAYHJBwa","name":"Read","input":{"file_path":"/boot/config/plugins/varaverk/Configurations/host2.conf"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7198,"cache_read_input_tokens":18241,"output_tokens":123,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":7198,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":123,"cache_read_input_tokens":18241,"cache_creation_input_tokens":7198,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7198},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRkJPRTEYYWEAtHwFQV","type":"assistant","uuid":"9baff7e3-2fc9-40a8-8064-4243f238149c","timestamp":"2026-06-06T17:48:52.654Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"9baff7e3-2fc9-40a8-8064-4243f238149c","isSidechain":false,"promptId":"0a108bc8-cb46-4a98-91cc-27f1cab8a949","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01JjUWsFnTZi8dnuZAYHJBwa","type":"tool_result","content":"1\t#!/bin/bash\n2\t# ==============================================================================================\n3\t# ========================== HOST2 CONFIGURATION — unRAID-Jayred365 ===========================\n4\t# ==============================================================================================\n5\t# HOST2-specific variables — credentials, container names, share paths, failover lists.\n6\t# Sourced after master.conf — values here extend shared profile arrays and add HOST2-specific\n7\t# identity, credentials, and container configuration.\n8\t#\n9\t# Sparse checkout (git) ensures HOST1 never receives this file.\n10\t# HOST1 never sees HOST2 credentials — clean separation at the file level.\n11\t#\n12\t# DO NOT put shared config here — thresholds, toggles, profiles belong in master.conf.\n13\t# DO NOT put HOST1 variables here — they belong in host1.conf.\n14\t#\n15\t# ── STATUS ────────────────────────────────────────────────────────────────────────────────────\n16\t# HOST2 is currently being rebuilt — most sections scaffolded, fill in when back online.\n17\t# When ready: set FALLBACK_ENABLED=true and DAILY_RSYNC_ENABLED=true in master.conf.\n18\t#\n19\t# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────\n20\t#\n21\t# ── IDENTITY & CONNECTIVITY ────────────────────────────────────────────────────────────────\n22\t# IDENTITY hostname, SSH key\n23\t# EMBY container name, URL, API key\n24\t# NOTIFICATIONS Discord webhook\n25\t# PARTNERSHIP auth containers, backup paths\n26\t#\n27\t# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────\n28\t# DAILY SYNC SHARES media shares HOST2 owns and pushes to HOST1\n29\t# WEEKLY SYNC SHARES appdata shares synced weekly (Sunday 2:30am)\n30\t# CRITICAL SYNC SHARES appdata shares synced every 30 minutes\n31\t# BACKUP VERIFY shares for checksum verification against remote\n32\t# HOST2 RSYNC PROFILE host2-appdata profile for HOST2-specific appdata syncs\n33\t#\n34\t# ── DOCKER ─────────────────────────────────────────────────────────────────────────────────\n35\t# DOCKER DAILY RESTART containers restarted daily\n36\t# DOCKER WEEKLY RESTART containers restarted weekly\n37\t# DOCKER WATCHDOG memory limits, health URLs, required containers, ignore list\n38\t# DOCKER NETWORK CONNECT networks and containers for docker_network_connect.sh\n39\t#\n40\t# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────\n41\t# DDNS DDNS containers managed by HOST2\n42\t# INTERNET LOSS containers stopped when internet is lost\n43\t# FALLBACK TIERS what HOST2 runs for HOST1 per tier\n44\t# TIER DELAYS how long HOST2 must be down before each tier activates on HOST1\n45\t# RSYNC WRITEBACK HOST2 appdata synced back on handback\n46\t#\n47\t# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────\n48\t# MEDIA PERMISSIONS share list for media_shares_permissions.sh\n49\t# MEDIA CLEANER folder lists for media_cleaner.sh\n50\t#\n51\t# ── MONITORS ───────────────────────────────────────────────────────────────────────────────\n52\t# CERTIFICATE MONITOR domains checked for SSL expiry\n53\t# SMART HEALTH drives to skip in SMART monitoring\n54\t# ZFS REPORT pools to exclude from ZFS health report\n55\t#\n56\t# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────\n57\t# TRANSCODES ramdisk size, thresholds, SSD path, server array\n58\t#\n59\t# ── ARR STACK ──────────────────────────────────────────────────────────────────────────────\n60\t# SONARR URL, API key, path map\n61\t# RADARR URL, API key, path map\n62\t# ARR RECOVERY per-arr recovery toggles (no Lidarr on HOST2)\n63\t#\n64\t# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────\n65\t# SYSTEM WATCHDOG per-host check toggles and NIC configuration\n66\t#\n67\t# ── RESOURCE MANAGER ───────────────────────────────────────────────────────────────────────\n68\t# RESOURCE MANAGER containers paused/stopped under memory pressure\n69\t#\n70\t# ==============================================================================================\n71\t\n72\t# ==============================================================================================\n73\t# ── IDENTITY & CONNECTIVITY ───────────────────────────────────────────────────────────────────\n74\t# ==============================================================================================\n75\t\n76\t# ━━━ Identity ━━━\n77\t# HOST2 hostname lives in master.conf (not a credential — safe for all servers).\n78\t# SSH key used for all server-to-server operations — rsync, failover container commands.\n79\t# Must be in /root/.ssh/ and authorised in HOST1's /root/.ssh/authorized_keys.\n80\t HOST2_SSH_KEY=\"/root/.ssh/Jayred365-rsync-key\"\n81\t HOST2_OWNER=\"jayred365\"\n82\t HOST2_OWNER_EMAIL=\"\" # fill in when HOST2 is back online\n83\t\n84\t# ━━━ Unraid API ━━━\n85\t# Generate in Unraid: Settings → Management Access → API Keys → + New Key\n86\t HOST2_UNRAID_API_KEY=\"2bdf5119d61eefa3023434748bd1c171bd23dc0b2ebc8586e24abe07df986acc\"\n87\t\n88\t# ━━━ Emby ━━━\n89\t# Referenced by transcode_manager.sh, emby_session_report.sh, emby_database_repair.sh,\n90\t# weekly_sync_maintenance.sh, and HOST2_TRANSCODE_SERVERS below.\n91\t# API key: Emby Dashboard → API Keys → + New Key\n92\t HOST2_EMBY_CONTAINER=\"Emby-Jayred365\"\n93\t HOST2_EMBY_URL=\"http://localhost:8096\"\n94\t HOST2_EMBY_API_KEY=\"your-host2-emby-api-key\"\n95\t\n96\t# ━━━ Jellyfin ━━━\n97\t# API key: Jellyfin Dashboard → Administration → API Keys → + New Key\n98\t HOST2_JELLYFIN_CONTAINER=\"Jellyfin\"\n99\t HOST2_JELLYFIN_URL=\"http://localhost:8095\"\n100\t HOST2_JELLYFIN_API_KEY=\"956d0168987f4e4680626653abb080f0\"\n101\t\n102\t# ━━━ Notifications ━━━\n103\t# Discord webhook — leave blank to disable.\n104\t# Per-host so HOST1 and HOST2 can post to different channels or only one server notifies.\n105\t HOST2_DISCORD_WEBHOOK=\"\"\n106\t\n107\t# ━━━ Partnership ━━━\n108\t# HOST2 is the mirror — HOST1 is always the owner unless --transfer has been run.\n109\t# See README-Partnership.md and master.conf PARTNERSHIP section for full lifecycle docs.\n110\t\n111\t# Auth containers reconfigured on onboard/offboard.\n112\t# Format: \"ContainerName|WebUIPort\"\n113\t# On onboard → WebUI pointed at owner's Tailscale IP (mirror clicks NPM, gets owner's auth)\n114\t# On offboard → WebUI pointed back at localhost\n115\t HOST2_PARTNERSHIP_AUTH_WEBUIS=(\n116\t # fill in when HOST2 is back online\n117\t # \"NginxProxyManager|81\"\n118\t )\n119\t\n120\t# Containers to stop on this server before the owner deploys the auth stack during onboard.\n121\t# List whatever auth/proxy containers are currently running here.\n122\t HOST2_PARTNERSHIP_REPLACE_CONTAINERS=(\n123\t \"NginxProxyManager\"\n124\t \"Authelia\"\n125\t \"Authelia-Secondary\"\n126\t \"Mariadb-Authelia\"\n127\t \"Mariadb-Authelia-Secondary\"\n128\t \"Redis-Authelia\"\n129\t \"Redis-Authelia-Secondary\"\n130\t \"Lldap-Gmer4Lfe\"\n131\t )\n132\t\n133\t# Arr containers to stop on this server before the owner deploys the arr stack during onboard.\n134\t HOST2_PARTNERSHIP_ARR_REPLACE_CONTAINERS=(\n135\t # \"Sonarr\"\n136\t # \"Radarr\"\n137\t # \"Lidarr\"\n138\t # \"Prowlarr\"\n139\t # \"Bazarr\"\n140\t )\n141\t\n142\t# Paths HOST1 should collect during the grace window after offboard.\n143\t# Notified on offboard — no auto-deletion, HOST1 must collect manually within PARTNERSHIP_GRACE_HOURS.\n144\t HOST2_PARTNERSHIP_MIRROR_BACKUPS=(\n145\t # fill in when HOST2 is back online\n146\t )\n147\t\n148\t# Containers parked on this server when partnership is active.\n149\t# Stopped on onboard (owner deploys its stack instead), restarted on offboard.\n150\t HOST2_PARTNERSHIP_OWN_CONTAINERS=(\n151\t # \"Emby\"\n152\t # \"NginxProxyManager\"\n153\t )\n154\t\n155\t# This server's desired Emby admin account on the shared Emby instance.\n156\t# Set these — owner reads them during --onboard to create the account.\n157\t HOST2_PARTNERSHIP_EMBY_ADMIN_USER=\"\" # desired Emby username\n158\t HOST2_PARTNERSHIP_EMBY_ADMIN_PASS=\"\" # desired Emby password\n159\t\n160\t# ==============================================================================================\n161\t# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────\n162\t# ==============================================================================================\n163\t\n164\t# ━━━ Daily Sync Shares ━━━\n165\t# Shares HOST2 pushes to all other nodes every night (1am via daily_sync_maintenance.sh).\n166\t# Mesh model: every node pushes every media share — no ownership, no mirrors.\n167\t# arr_sync ensures all arr libraries converge (union). rsync spreads files (additive, no --delete).\n168\t# arr_cleanup removes true orphans based on local arr state.\n169\t# Any node can download content to any share — it propagates to all nodes on the next cycle.\n170\t# Nextcloud excluded — personal data, not arr-managed, synced HOST1→HOST2 only as offsite backup.\n171\t# Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed.\n172\t# For shares needing container stops or custom options — add a profile in master.conf.\n173\t HOST2_DAILY_SYNC_SHARES=(\n174\t /mnt/user/Anime_Movies\n175\t /mnt/user/Anime_Shows\n176\t /mnt/user/Books\n177\t /mnt/user/Intros\n178\t /mnt/user/Kids_Movies\n179\t /mnt/user/Kids_Tv_Shows\n180\t /mnt/user/Movies\n181\t /mnt/user/Music\n182\t /mnt/user/Music_Videos\n183\t /mnt/user/stand-up_comedy\n184\t /mnt/user/Sports\n185\t /mnt/user/Tv_Shows\n186\t /mnt/user/Anime_Shows-Old\n187\t /mnt/user/Anime_Movies-Old\n188\t )\n189\t\n190\t# Personal encrypted shares — synced for offsite backup, independent of media shares.\n191\t# ZFS encrypted at dataset level — remote receives encrypted blocks, cannot read content.\n192\t# See README-Rsync_Setup.md for ZFS encryption setup before uncommenting.\n193\t HOST2_PERSONAL_SHARES=(\n194\t # /mnt/user/HOST2-Personal # uncomment after creating encrypted dataset\n195\t )\n196\t\n197\t# ━━━ Weekly Sync Shares ━━━\n198\t# Appdata shares synced during the weekly maintenance window (Sunday 2:30am).\n199\t# Containers stopped both sides before sync — full clean state guaranteed.\n200\t# Profiles drive container stops, excludes, and options — configured in master.conf RSYNC section.\n201\t HOST2_WEEKLY_SYNC_SHARES=(\n202\t # fill in when HOST2 is back online\n203\t # \"/mnt/user/Media_Server/Emby\"\n204\t # \"/mnt/user/appdata-Fallback/Critical-Data\"\n205\t )\n206\t\n207\t# ━━━ Intermediate Sync Shares ━━━\n208\t# Shares synced every 4 hours by intermediate_sync_maintenance.sh.\n209\t# Uses DEFAULT_RSYNC_OPTS (no --delete) — for sub-daily propagation of metadata or watch state.\n210\t# Full media share sync stays in the daily window. Leave empty to skip mid-day rsync entirely.\n211\t HOST2_INTERMEDIATE_SYNC_SHARES=(\n212\t # fill in when HOST2 is back online\n213\t # Example: \"/mnt/user/Emby_Metadata\"\n214\t )\n215\t\n216\t# ━━━ Critical Sync Shares ━━━\n217\t# Appdata shares synced every 30 minutes by critical_sync_maintenance.sh.\n218\t# Format: \"/path/to/share\" or \"/path/to/share|profile-name\"\n219\t HOST2_CRITICAL_SYNC_SHARES=(\n220\t # fill in when HOST2 is back online\n221\t # \"/mnt/user/appdata-Fallback/Critical-Data|critical-fallback\"\n222\t # \"/mnt/user/Media_Server/Emby|emby-fallback\"\n223\t )\n224\t\n225\t# ━━━ Backup Verify ━━━\n226\t# Shares verified by backup_verify.sh — random file checksum comparison against remote.\n227\t# Leave empty to use HOST2_DAILY_SYNC_SHARES automatically.\n228\t# Sample size and minimum file size defined in master.conf.\n229\t HOST2_BACKUP_VERIFY_SHARES=(\n230\t # leave empty to use HOST2_DAILY_SYNC_SHARES automatically\n231\t )\n232\t\n233\t# ━━━ HOST2 Rsync Profile — host2-appdata ━━━\n234\t# HOST2-specific appdata sync profile — extends the shared PROFILE_* arrays in master.conf.\n235\t# Use for appdata unique to HOST2.\n236\t# Shared appdata (auth stack, Emby) use dedicated profiles defined in master.conf.\n237\t# Run manually: bash Rsync/rsync.sh /mnt/user/appdata-Fallback/HOST2-Appdata --profile=host2-appdata\n238\t PROFILE_RSYNC_OPTS[host2-appdata]=\"-av --info=progress2 --bwlimit=${PROFILE_BW_LIMIT[host2-appdata]:-8000}\"\n239\t PROFILE_BW_LIMIT[host2-appdata]=8000\n240\t PROFILE_RETRY_COUNT[host2-appdata]=3\n241\t PROFILE_SLEEP[host2-appdata]=300\n242\t PROFILE_CRITICAL_CONTAINER_NAMES[host2-appdata]=\"\" # fill in when HOST2 is back online\n243\t PROFILE_DELAYED_CONTAINERS[host2-appdata]=\"\"\n244\t PROFILE_CONTAINER_DELAY[host2-appdata]=5\n245\t PROFILE_EXCLUDE_DIRS[host2-appdata]=\"logs *.tmp\"\n246\t\n247\t# ==============================================================================================\n248\t# ── DOCKER ────────────────────────────────────────────────────────────────────────────────────\n249\t# ==============================================================================================\n250\t\n251\t# ━━━ Docker Daily Restart ━━━\n252\t# Containers restarted every day via DAILY_MAINTENANCE_SCRIPTS.\n253\t# Fill in when HOST2 is back online — add containers that degrade without daily restart.\n254\t HOST2_DAILY_RESTART_CONTAINERS=(\n255\t \"NginxProxyManager\"\n256\t # add HOST2 daily restart containers here\n257\t )\n258\t\n259\t# ━━━ Docker Weekly Restart ━━━\n260\t# Less critical services restarted weekly via WEEKLY_MAINTENANCE_SCRIPTS (Sunday 2:30am).\n261\t# Containers already stopped for weekly sync — restart adds zero extra downtime.\n262\t HOST2_WEEKLY_RESTART_CONTAINERS=(\n263\t # add HOST2 weekly restart containers here\n264\t )\n265\t\n266\t# ━━━ Docker Watchdog ━━━\n267\t# Per-HOST2 container configuration for docker_watchdog.sh.\n268\t# Shared thresholds and toggles live in master.conf.\n269\t\n270\t# Memory hard limits in MB — immediate restart if exceeded.\n271\t# Set at \"container is clearly broken\" not \"container is busy\".\n272\t# 20GB=20480 16GB=16384 12GB=12288 10GB=10240 8GB=8192 4GB=4096 2GB=2048 1GB=1024\n273\t declare -A HOST2_WATCHDOG_CONTAINERS=(\n274\t [\"Emby\"]=16384 # fill in correct limit when HOST2 is back online\n275\t )\n276\t\n277\t# HTTP health check URLs — checked every cycle, strike system before restart.\n278\t# Only add containers with a meaningful web interface to check.\n279\t declare -A HOST2_WATCHDOG_CONTAINER_URLS=(\n280\t [\"Emby\"]=\"http://localhost:8096\"\n281\t )\n282\t\n283\t# Required containers — must always be running on HOST2.\n284\t# Strike system before restart — repeated failures go on skip list, auto-clears on recovery.\n285\t# Listed in dependency order — dependencies before dependents.\n286\t HOST2_WATCHDOG_REQUIRED_CONTAINERS=(\n287\t \"NginxProxyManager\"\n288\t # add HOST2 required containers here when back online\n289\t )\n290\t\n291\t# Containers to skip in Tier 2 global scan — legitimately stopped or frequently restarting.\n292\t# Watchdog leaves these alone entirely — no restart attempts, no crash loop tracking.\n293\t HOST2_WATCHDOG_SCAN_IGNORE=(\n294\t # add HOST2 scan ignore containers here when back online\n295\t )\n296\t\n297\t# Dependency ordering — skip restarting a container if its dependency is also down.\n298\t# Prevents watchdog from restarting dependent services before their dependencies are up.\n299\t# SPACE-SEPARATED STRINGS — converted to array at runtime.\n300\t declare -A HOST2_WATCHDOG_DEPENDENCIES=(\n301\t # add HOST2 dependencies here when containers are defined\n302\t # [\"Authelia\"]=\"Mariadb-Authelia Redis-Authelia\"\n303\t )\n304\t\n305\t# Per-container appdata growth suppress ceilings in MB.\n306\t# ONLY needed in specific cases — growth rate detection covers all containers automatically.\n307\t# Use when a container legitimately has large stable data and you want to suppress false-positive\n308\t# growth alerts. Add entries here only when a container triggers warnings it shouldn't.\n309\t declare -A HOST2_WATCHDOG_APPDATA_SIZES=(\n310\t # add HOST2 suppress entries here only as needed\n311\t )\n312\t\n313\t# ━━━ Docker Network Connect ━━━\n314\t# Containers connected to custom networks at array start by docker_network_connect.sh.\n315\t# Networks created if they don't exist — idempotent, safe to re-run.\n316\t HOST2_NETWORK_CONNECT_CONTAINERS=(\n317\t # fill in when HOST2 is back online\n318\t )\n319\t\n320\t HOST2_NETWORK_CONNECT_NETWORKS=(\n321\t # fill in when HOST2 is back online\n322\t )\n323\t\n324\t# ==============================================================================================\n325\t# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────\n326\t# ==============================================================================================\n327\t\n328\t# ━━━ DDNS ━━━\n329\t# DDNS containers HOST2 manages — started/stopped by fallback.sh per DDNS absolute rules:\n330\t# Internet loss → stop immediately\n331\t# Failover → HOST1 starts HOST2's DDNS as Tier 1 (before any other containers)\n332\t# Handback → stop HOST2's DDNS on HOST1 → rsync → start containers → start local DDNS last\n333\t HOST2_DDNS_CONTAINERS=(\n334\t \"Gmer4Lfe.us\"\n335\t )\n336\t\n337\t# ━━━ Internet Loss ━━━\n338\t# Containers stopped immediately on HOST2 when internet connection is lost.\n339\t# Prevents external-facing services from operating without connectivity.\n340\t FALLBACK_HOST2_STOP_ON_NO_NET=(\n341\t \"Gmer4Lfe.us\"\n342\t )\n343\t\n344\t# ━━━ Fallback Tiers — HOST2 Runs for HOST1 ━━━\n345\t# Containers HOST2 starts when HOST1 goes down.\n346\t# Tier 1 is always immediate — vital services cannot wait.\n347\t# Higher tiers activate after HOST1_TIER*_DELAY minutes (set in host1.conf).\n348\t FALLBACK_HOST2_COVERS_HOST1_TIER1=(\n349\t \"Gmer4Lfe.com\"\n350\t \"Gitea\" # source of truth — must be reachable even when HOST1 auth stack is down\n351\t \"Emby\"\n352\t \"VaultWarden-Gmer4Lfe\"\n353\t \"Dispatcharr\"\n354\t \"Dispatcharr-Basic\"\n355\t \"Dispatcharr-Iptv-Users\"\n356\t \"ErsatzTV-Emby\"\n357\t )\n358\t\n359\t FALLBACK_HOST2_COVERS_HOST1_TIER2=(\n360\t \"Postgres-NextCloud\"\n361\t \"NextCloud\"\n362\t \"PostgreSQL_Immich\"\n363\t \"Immich-Gmer4Lfe\"\n364\t )\n365\t\n366\t FALLBACK_HOST2_COVERS_HOST1_TIER3=(\n367\t \"Gitea\"\n368\t )\n369\t\n370\t FALLBACK_HOST2_COVERS_HOST1_TIER4=(\n371\t \"Sonarr\"\n372\t \"Radarr\"\n373\t \"Lidarr\"\n374\t \"Readarr\"\n375\t \"Prowlarr\"\n376\t \"Bazarr\"\n377\t \"SABnzbd-Gmer4Lfe\"\n378\t \"Qbittorrent-Gmer4Lfe\"\n379\t \"LidaTube\"\n380\t \"Pinchflat\"\n381\t \"ChannelTube\"\n382\t )\n383\t\n384\t# ━━━ Tier Delays — HOST2's Containers on HOST1 ━━━\n385\t# How long HOST2 must be down before each tier activates on HOST1 — in minutes.\n386\t# Tier 1 is always immediate — no delay var needed.\n387\t HOST2_TIER2_DELAY=240 # 4 hours — productivity services\n388\t HOST2_TIER3_DELAY=720 # 12 hours — secondary services\n389\t HOST2_TIER4_DELAY=1440 # 24 hours — arrs + downloaders\n390\t\n391\t# ━━━ Rsync Writeback — HOST2 Appdata Back on Handback ━━━\n392\t# Syncs HOST2 appdata BACK to HOST2 when it comes back online after a failover.\n393\t# Containers stopped before writeback — clean source, no competing writes.\n394\t#\n395\t# HOST2_TIER1_WRITEBACK_DELAY: short outages skip Tier 1 writeback — primary state\n396\t# is more reliable than dirty sync data for brief outages.\n397\t HOST2_TIER1_WRITEBACK_DELAY=60 # skip writeback if outage under 1hr\n398\t\n399\t# Tier 4 automatically syncs HOST2_DAILY_SYNC_SHARES — only list paths NOT in that array.\n400\t FALLBACK_HOST2_WRITEBACK_TIER1=(\n401\t # \"/mnt/user/appdata-Fallback/Jayred365-Emby\"\n402\t )\n403\t\n404\t FALLBACK_HOST2_WRITEBACK_TIER2=(\n405\t # \"/mnt/user/appdata-Fallback/Jayred365-Important\"\n406\t )\n407\t\n408\t FALLBACK_HOST2_WRITEBACK_TIER3=(\n409\t # \"location-placeholder\"\n410\t )\n411\t\n412\t FALLBACK_HOST2_WRITEBACK_TIER4=(\n413\t \"/mnt/user/appdata-Fallback/Arrs_Stack\" # arr databases — downloads queued during outage\n414\t )\n415\t\n416\t# ==============================================================================================\n417\t# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────\n418\t# ==============================================================================================\n419\t\n420\t# ━━━ Media Permissions ━━━\n421\t# Shares that media_shares_permissions.sh applies PERMISSIONS_MODE and PERMISSIONS_OWNER to.\n422\t# Runs first in DAILY_MAINTENANCE_SCRIPTS — arr cleanup depends on correct ownership.\n423\t HOST2_MEDIA_PERMISSION_SHARES=(\n424\t /mnt/user/Anime_Movies\n425\t /mnt/user/Anime_Shows\n426\t )\n427\t\n428\t# ━━━ Media Cleaner ━━━\n429\t# Folder lists for media_cleaner.sh — two profiles: anime and media.\n430\t# File patterns shared across all servers — defined in master.conf.\n431\t# Called via DAILY_MAINTENANCE_SCRIPTS. Run manually: Media/media_cleaner.sh anime|media\n432\t HOST2_ANIME_CLEAN_FOLDERS=(\n433\t /mnt/user/Anime_Movies\n434\t /mnt/user/Anime_Shows\n435\t )\n436\t\n437\t HOST2_MEDIA_CLEAN_FOLDERS=(\n438\t # fill in when HOST2 is back online\n439\t )\n440\t\n441\t# ==============================================================================================\n442\t# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────\n443\t# ==============================================================================================\n444\t\n445\t# ━━━ Certificate Monitor ━━━\n446\t# Domains checked via direct openssl connection — not relying on NPM's certificate state.\n447\t# Checks the actual certificate served, not what NPM thinks it has.\n448\t# Thresholds (CERT_WARN_DAYS, CERT_CRIT_DAYS) defined in master.conf.\n449\t HOST2_CERT_MONITOR_DOMAINS=(\n450\t # fill in when HOST2 is back online\n451\t )\n452\t\n453\t# ━━━ SMART Health ━━━\n454\t# Drives skipped in SMART attribute monitoring — hardware is server-specific.\n455\t# Thresholds read from dynamix.cfg at runtime — fallbacks in master.conf.\n456\t HOST2_SMART_IGNORE_DRIVES=(\n457\t \"sda\" # boot USB — SMART not meaningful on flash drives\n458\t )\n459\t\n460\t# ━━━ ZFS Report ━━━\n461\t# Pools excluded from the weekly ZFS health report — reduces noise from single-disk array pools.\n462\t# Pool health thresholds defined in master.conf.\n463\t HOST2_ZFS_REPORT_IGNORE_POOLS=(\n464\t # fill in when HOST2 is back online\n465\t )\n466\t\n467\t# ==============================================================================================\n468\t# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────\n469\t# ==============================================================================================\n470\t\n471\t# Ramdisk size ceiling — tmpfs only uses RAM actually needed, not the full size upfront.\n472\t# Adjust HOST2_RAMDISK_WARN_GB and HOST2_RAMDISK_LOW_GB together if this changes.\n473\t HOST2_RAMDISK_SIZE=\"8G\"\n474\t\n475\t# Usage thresholds — coupled to HOST2_RAMDISK_SIZE, adjust all three together if size changes.\n476\t# Hysteresis gap (6.8 - 5.5 = 1.3GB) prevents flip-flop between ramdisk and SSD.\n477\t HOST2_RAMDISK_WARN_GB=6.8 # flip to SSD when ramdisk usage reaches this\n478\t HOST2_RAMDISK_LOW_GB=5.5 # flip back to ramdisk when usage drops to this\n479\t\n480\t# SSD fallback path — where transcodes land when ramdisk exceeds HOST2_RAMDISK_WARN_GB.\n481\t# Must be on cache pool — array disks too slow for active transcode writes.\n482\t HOST2_TRANSCODE_SSD=\"/mnt/cache/Temp_Storage/Emby/Transcodes/\"\n483\t\n484\t# Media servers sharing the ramdisk transcode space on HOST2.\n485\t# Format: \"ContainerName|URL|APIKey|Type\" — Type: emby | jellyfin | plex\n486\t# Entries with placeholder API keys are skipped automatically.\n487\t# ⚠️ Tdarr does NOT belong here — keep Tdarr on SSD, not ramdisk.\n488\t HOST2_TRANSCODE_SERVERS=(\n489\t \"${HOST2_EMBY_CONTAINER}|${HOST2_EMBY_URL}|${HOST2_EMBY_API_KEY}|emby\"\n490\t \"${HOST2_JELLYFIN_CONTAINER}|${HOST2_JELLYFIN_URL}|${HOST2_JELLYFIN_API_KEY}|jellyfin\"\n491\t )\n492\t\n493\t# ==============================================================================================\n494\t# ── ARR STACK ─────────────────────────────────────────────────────────────────────────────────\n495\t# ==============================================================================================\n496\t# Used by arr cleanup scripts and arrs_failed_stalled_recovery.sh.\n497\t# detect_hosts() selects HOST2 vars when running on HOST2.\n498\t# Lidarr does not run on HOST2 — HOST1_LIDARR_RECOVERY flag handles the exit cleanly.\n499\t#\n500\t# PATH MAPS — container path → host path translation.\n501\t# Arr stores file paths using container-internal paths — scripts need host paths to scan.\n502\t# Add one entry per root folder in arr Settings → Media Management → Root Folders.\n503\t HOST2_FANART_API_KEY=\"Yd7147a43b692df0b364b94dc47efb81\"\n504\t HOST2_LASTFM_API_KEY=\"be6dc169c33ae263e690c30d18b7491d\"\n505\t\n506\t# ━━━ Sonarr ━━━\n507\t HOST2_SONARR_URL=\"http://localhost:8989\"\n508\t HOST2_SONARR_API_KEY=\"130decd3db5b4c25afad64864cd03f9f\"\n509\t HOST2_SONARR_TV_ROOT=\"/mnt/user/Anime_Shows\"\n510\t\n511\t declare -A HOST2_SONARR_PATH_MAP=(\n512\t # fill in when HOST2 is back online\n513\t # [\"/tv\"]=\"/mnt/user/Anime_Shows\"\n514\t )\n515\t\n516\t# ━━━ Radarr ━━━\n517\t HOST2_RADARR_URL=\"http://localhost:7878\"\n518\t HOST2_RADARR_API_KEY=\"d43a3ec6cf1549edb4af0cc63f98b2a9\"\n519\t HOST2_RADARR_MOVIES_ROOT=\"/mnt/user/Anime_Movies\"\n520\t\n521\t declare -A HOST2_RADARR_PATH_MAP=(\n522\t # fill in when HOST2 is back online\n523\t # [\"/anime-movies\"]=\"/mnt/user/Anime_Movies\"\n524\t )\n525\t\n526\t# ━━━ Arr Recovery Toggles ━━━\n527\t# false = skip that arr on this host — exits cleanly without error\n528\t HOST2_SONARR_RECOVERY=true\n529\t HOST2_RADARR_RECOVERY=true\n530\t # HOST2_LIDARR_RECOVERY not set — Lidarr does not run on HOST2\n531\t\n532\t# ==============================================================================================\n533\t# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────\n534\t# ==============================================================================================\n535\t# Per-host check toggles and NIC config for system_watchdog.sh.\n536\t# Aliased by detect_hosts() — script uses unprefixed SYS_WATCHDOG_* names.\n537\t# HOST2: i5 10th gen 64GB — being rebuilt, lighter workload, no ZFS cache pools.\n538\t#\n539\t# Conservative defaults during rebuild — re-enable checks as HOST2 stabilises.\n540\t# Three-tier response — all critical checks enabled regardless of rebuild state:\n541\t# Tier 1 (bypass strikes, reboot now): docker daemon, rootfs full, kernel oops, FD, /boot\n542\t# Tier 2 (bypass strikes with OOM): RAM critical + OOM kills in cycle\n543\t# Tier 3 (standard strike system): selectively disabled during rebuild\n544\t#\n545\t# RAM tiers, OOM limits, and reboot loop settings in master.conf System Watchdog section.\n546\t\n547\t# ━━━ Primary NIC ━━━\n548\t# Network interface for NIC state check — verify with: ip link show | grep \"^[0-9]\"\n549\t# Common values: eth0, bond0, br0, eno1\n550\t HOST2_SYS_WATCHDOG_NIC=\"eth0\"\n551\t\n552\t# ━━━ Tier 1 — Critical Checks ━━━\n553\t# All critical checks always enabled — these protect against acute failure regardless of\n554\t# rebuild state. Disabling any is not recommended.\n555\t\n556\t# Docker daemon unresponsive → try restart, reboot if restart fails.\n557\t HOST2_SYS_WATCHDOG_CHECK_DOCKER_DAEMON=true\n558\t\n559\t# rootfs at critical threshold (ROOTFS_CRITICAL_PCT=99) → reboot immediately.\n560\t HOST2_SYS_WATCHDOG_CHECK_ROOTFS=true\n561\t\n562\t# Kernel BUG/Oops in dmesg delta since last cycle → reboot immediately.\n563\t HOST2_SYS_WATCHDOG_CHECK_KERNEL_OOPS=true\n564\t\n565\t# File descriptor exhaustion at FD_CRITICAL_PCT (95%) → reboot immediately.\n566\t HOST2_SYS_WATCHDOG_CHECK_FD=true\n567\t\n568\t# /boot read-only detected → reboot immediately.\n569\t HOST2_SYS_WATCHDOG_CHECK_BOOT=true\n570\t\n571\t# ━━━ Tier 2 — Urgent OOM Check ━━━\n572\t# Both must be enabled for Tier 2 bypass to function.\n573\t\n574\t# Track kernel OOM kills each cycle via /proc/vmstat oom_kill delta.\n575\t HOST2_SYS_WATCHDOG_CHECK_OOM=true\n576\t\n577\t# Free RAM check — 64GB RAM on HOST2, tiers adjusted relative to HOST1.\n578\t# Update master.conf SYS_WATCHDOG_MEM_* thresholds if HOST2 needs different values.\n579\t# Currently inheriting shared master.conf values — may want lower thresholds on 64GB.\n580\t HOST2_SYS_WATCHDOG_CHECK_RAM=true\n581\t\n582\t# ━━━ Tier 3 — Standard Checks (strike system) ━━━\n583\t# Several checks disabled during rebuild — enable progressively as HOST2 stabilises.\n584\t# Each check must fail SYS_WATCHDOG_STRIKE_LIMIT consecutive cycles before action.\n585\t\n586\t# /var/log filesystem usage above SYS_WATCHDOG_LOG_PCT.\n587\t HOST2_SYS_WATCHDOG_CHECK_LOG=true\n588\t\n589\t# ZFS ARC memory check.\n590\t# DISABLED — HOST2 has no ZFS cache pools. Enable if ZFS pools are added later.\n591\t HOST2_SYS_WATCHDOG_CHECK_ARC=false\n592\t\n593\t# CPU temperature above SYS_WATCHDOG_CPU_TEMP_MAX (95°C).\n594\t HOST2_SYS_WATCHDOG_CHECK_CPU_TEMP=true\n595\t\n596\t# Load average above SYS_WATCHDOG_LOAD_MULTIPLIER × core count.\n597\t# DISABLED — rebuild operations cause legitimate load spikes. Enable after rebuild.\n598\t HOST2_SYS_WATCHDOG_CHECK_LOAD=false\n599\t\n600\t# Zombie process count above SYS_WATCHDOG_ZOMBIE_LIMIT (50).\n601\t HOST2_SYS_WATCHDOG_CHECK_ZOMBIES=true\n602\t\n603\t# docker_watchdog.sh persistent skip list check.\n604\t# DISABLED during rebuild — skip list may be unreliable mid-rebuild, avoid false reboots.\n605\t# Enable once HOST2 is fully operational and docker_watchdog.sh is running stably.\n606\t HOST2_SYS_WATCHDOG_CHECK_CONTAINERS=false\n607\t\n608\t# /tmp filesystem usage with auto-clear attempt.\n609\t HOST2_SYS_WATCHDOG_CHECK_TMP=true\n610\t\n611\t# Array disk error count delta in /proc/mdstat.\n612\t HOST2_SYS_WATCHDOG_CHECK_MDSTAT=true\n613\t\n614\t# Primary NIC operstate — uses HOST2_SYS_WATCHDOG_NIC above.\n615\t HOST2_SYS_WATCHDOG_CHECK_NETWORK=true\n616\t\n617\t# sshd running check — restart attempt before escalating.\n618\t HOST2_SYS_WATCHDOG_CHECK_SSHD=true\n619\t\n620\t# Runaway process detection.\n621\t# DISABLED — rebuild workloads may legitimately peg CPU. Enable after rebuild.\n622\t HOST2_SYS_WATCHDOG_CHECK_RUNAWAY=false\n623\t\n624\t# ==============================================================================================\n625\t# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────\n626\t# ==============================================================================================\n627\t# Containers to manage under pressure — see master.conf RW_CRITICAL_CONTAINERS for exclusions.\n628\t\n629\t# docker pause at medium pressure (RAM < RW_RAM_MEDIUM_GB or load > medium threshold)\n630\t# Suspended in-place — instant to pause/unpause, no state lost, no restart delay.\n631\t HOST2_RW_PAUSE_CONTAINERS=(\n632\t # fill in when HOST2 is back online\n633\t )\n634\t\n635\t# docker stop at hard pressure (RAM < RW_RAM_HARD_GB)\n636\t# Full stop — these are optional/heavy services that free significant RAM when stopped.\n637\t# resource_watchdog.sh restarts them when pressure fully clears (RAM >= RW_RAM_RECOVER_GB).\n638\t HOST2_RW_STOP_CONTAINERS=(\n639\t # fill in when HOST2 is back online\n640\t )\n641\t\n642\t# ==============================================================================================\n643\t# ── AUTH STACK ────────────────────────────────────────────────────────────────────────────────\n644\t# ==============================================================================================\n645\t# Credentials for the Varaverk Auth Stack page (NPM, lldap, Authelia).\n646\t# Fill in when HOST2 is back online.\n647\t\n648\t# ━━━ NginxProxyManager ━━━\n649\t HOST2_NPM_URL=\"http://localhost:81\"\n650\t HOST2_NPM_USER=\"\" # NPM admin email\n651\t HOST2_NPM_PASS=\"\" # NPM admin password\n652\t\n653\t# ━━━ lldap ━━━\n654\t HOST2_LLDAP_URL=\"http://localhost:17170\"\n655\t HOST2_LLDAP_USER=\"admin\" # lldap admin username\n656\t HOST2_LLDAP_PASS=\"\" # lldap admin password\n657\t\n658\t# ━━━ Authelia ━━━\n659\t HOST2_AUTHELIA_CONFIG=\"/mnt/user/appdata-Fallback/Critical-Data/Authelia/configuration.yml\"\n660\t HOST2_AUTHELIA_CONTAINER=\"Authelia\"\n661\t\n662\t# ==============================================================================================\n663\t# ──────────────────────── End Of HOST2 Variables ──────────────────────────────────────────────\n664\t# =============================================================================================="}]},"uuid":"8478fe05-f414-45a6-b9e8-8bae17c592fb","timestamp":"2026-06-06T17:48:52.812Z","toolUseResult":{"type":"text","file":{"filePath":"/boot/config/plugins/varaverk/Configurations/host2.conf","content":"#!/bin/bash\n# ==============================================================================================\n# ========================== HOST2 CONFIGURATION — unRAID-Jayred365 ===========================\n# ==============================================================================================\n# HOST2-specific variables — credentials, container names, share paths, failover lists.\n# Sourced after master.conf — values here extend shared profile arrays and add HOST2-specific\n# identity, credentials, and container configuration.\n#\n# Sparse checkout (git) ensures HOST1 never receives this file.\n# HOST1 never sees HOST2 credentials — clean separation at the file level.\n#\n# DO NOT put shared config here — thresholds, toggles, profiles belong in master.conf.\n# DO NOT put HOST1 variables here — they belong in host1.conf.\n#\n# ── STATUS ────────────────────────────────────────────────────────────────────────────────────\n# HOST2 is currently being rebuilt — most sections scaffolded, fill in when back online.\n# When ready: set FALLBACK_ENABLED=true and DAILY_RSYNC_ENABLED=true in master.conf.\n#\n# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────\n#\n# ── IDENTITY & CONNECTIVITY ────────────────────────────────────────────────────────────────\n# IDENTITY hostname, SSH key\n# EMBY container name, URL, API key\n# NOTIFICATIONS Discord webhook\n# PARTNERSHIP auth containers, backup paths\n#\n# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────\n# DAILY SYNC SHARES media shares HOST2 owns and pushes to HOST1\n# WEEKLY SYNC SHARES appdata shares synced weekly (Sunday 2:30am)\n# CRITICAL SYNC SHARES appdata shares synced every 30 minutes\n# BACKUP VERIFY shares for checksum verification against remote\n# HOST2 RSYNC PROFILE host2-appdata profile for HOST2-specific appdata syncs\n#\n# ── DOCKER ─────────────────────────────────────────────────────────────────────────────────\n# DOCKER DAILY RESTART containers restarted daily\n# DOCKER WEEKLY RESTART containers restarted weekly\n# DOCKER WATCHDOG memory limits, health URLs, required containers, ignore list\n# DOCKER NETWORK CONNECT networks and containers for docker_network_connect.sh\n#\n# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────\n# DDNS DDNS containers managed by HOST2\n# INTERNET LOSS containers stopped when internet is lost\n# FALLBACK TIERS what HOST2 runs for HOST1 per tier\n# TIER DELAYS how long HOST2 must be down before each tier activates on HOST1\n# RSYNC WRITEBACK HOST2 appdata synced back on handback\n#\n# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────\n# MEDIA PERMISSIONS share list for media_shares_permissions.sh\n# MEDIA CLEANER folder lists for media_cleaner.sh\n#\n# ── MONITORS ───────────────────────────────────────────────────────────────────────────────\n# CERTIFICATE MONITOR domains checked for SSL expiry\n# SMART HEALTH drives to skip in SMART monitoring\n# ZFS REPORT pools to exclude from ZFS health report\n#\n# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────\n# TRANSCODES ramdisk size, thresholds, SSD path, server array\n#\n# ── ARR STACK ──────────────────────────────────────────────────────────────────────────────\n# SONARR URL, API key, path map\n# RADARR URL, API key, path map\n# ARR RECOVERY per-arr recovery toggles (no Lidarr on HOST2)\n#\n# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────\n# SYSTEM WATCHDOG per-host check toggles and NIC configuration\n#\n# ── RESOURCE MANAGER ───────────────────────────────────────────────────────────────────────\n# RESOURCE MANAGER containers paused/stopped under memory pressure\n#\n# ==============================================================================================\n\n# ==============================================================================================\n# ── IDENTITY & CONNECTIVITY ───────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Identity ━━━\n# HOST2 hostname lives in master.conf (not a credential — safe for all servers).\n# SSH key used for all server-to-server operations — rsync, failover container commands.\n# Must be in /root/.ssh/ and authorised in HOST1's /root/.ssh/authorized_keys.\n HOST2_SSH_KEY=\"/root/.ssh/Jayred365-rsync-key\"\n HOST2_OWNER=\"jayred365\"\n HOST2_OWNER_EMAIL=\"\" # fill in when HOST2 is back online\n\n# ━━━ Unraid API ━━━\n# Generate in Unraid: Settings → Management Access → API Keys → + New Key\n HOST2_UNRAID_API_KEY=\"2bdf5119d61eefa3023434748bd1c171bd23dc0b2ebc8586e24abe07df986acc\"\n\n# ━━━ Emby ━━━\n# Referenced by transcode_manager.sh, emby_session_report.sh, emby_database_repair.sh,\n# weekly_sync_maintenance.sh, and HOST2_TRANSCODE_SERVERS below.\n# API key: Emby Dashboard → API Keys → + New Key\n HOST2_EMBY_CONTAINER=\"Emby-Jayred365\"\n HOST2_EMBY_URL=\"http://localhost:8096\"\n HOST2_EMBY_API_KEY=\"your-host2-emby-api-key\"\n\n# ━━━ Jellyfin ━━━\n# API key: Jellyfin Dashboard → Administration → API Keys → + New Key\n HOST2_JELLYFIN_CONTAINER=\"Jellyfin\"\n HOST2_JELLYFIN_URL=\"http://localhost:8095\"\n HOST2_JELLYFIN_API_KEY=\"956d0168987f4e4680626653abb080f0\"\n\n# ━━━ Notifications ━━━\n# Discord webhook — leave blank to disable.\n# Per-host so HOST1 and HOST2 can post to different channels or only one server notifies.\n HOST2_DISCORD_WEBHOOK=\"\"\n\n# ━━━ Partnership ━━━\n# HOST2 is the mirror — HOST1 is always the owner unless --transfer has been run.\n# See README-Partnership.md and master.conf PARTNERSHIP section for full lifecycle docs.\n\n# Auth containers reconfigured on onboard/offboard.\n# Format: \"ContainerName|WebUIPort\"\n# On onboard → WebUI pointed at owner's Tailscale IP (mirror clicks NPM, gets owner's auth)\n# On offboard → WebUI pointed back at localhost\n HOST2_PARTNERSHIP_AUTH_WEBUIS=(\n # fill in when HOST2 is back online\n # \"NginxProxyManager|81\"\n )\n\n# Containers to stop on this server before the owner deploys the auth stack during onboard.\n# List whatever auth/proxy containers are currently running here.\n HOST2_PARTNERSHIP_REPLACE_CONTAINERS=(\n \"NginxProxyManager\"\n \"Authelia\"\n \"Authelia-Secondary\"\n \"Mariadb-Authelia\"\n \"Mariadb-Authelia-Secondary\"\n \"Redis-Authelia\"\n \"Redis-Authelia-Secondary\"\n \"Lldap-Gmer4Lfe\"\n )\n\n# Arr containers to stop on this server before the owner deploys the arr stack during onboard.\n HOST2_PARTNERSHIP_ARR_REPLACE_CONTAINERS=(\n # \"Sonarr\"\n # \"Radarr\"\n # \"Lidarr\"\n # \"Prowlarr\"\n # \"Bazarr\"\n )\n\n# Paths HOST1 should collect during the grace window after offboard.\n# Notified on offboard — no auto-deletion, HOST1 must collect manually within PARTNERSHIP_GRACE_HOURS.\n HOST2_PARTNERSHIP_MIRROR_BACKUPS=(\n # fill in when HOST2 is back online\n )\n\n# Containers parked on this server when partnership is active.\n# Stopped on onboard (owner deploys its stack instead), restarted on offboard.\n HOST2_PARTNERSHIP_OWN_CONTAINERS=(\n # \"Emby\"\n # \"NginxProxyManager\"\n )\n\n# This server's desired Emby admin account on the shared Emby instance.\n# Set these — owner reads them during --onboard to create the account.\n HOST2_PARTNERSHIP_EMBY_ADMIN_USER=\"\" # desired Emby username\n HOST2_PARTNERSHIP_EMBY_ADMIN_PASS=\"\" # desired Emby password\n\n# ==============================================================================================\n# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Daily Sync Shares ━━━\n# Shares HOST2 pushes to all other nodes every night (1am via daily_sync_maintenance.sh).\n# Mesh model: every node pushes every media share — no ownership, no mirrors.\n# arr_sync ensures all arr libraries converge (union). rsync spreads files (additive, no --delete).\n# arr_cleanup removes true orphans based on local arr state.\n# Any node can download content to any share — it propagates to all nodes on the next cycle.\n# Nextcloud excluded — personal data, not arr-managed, synced HOST1→HOST2 only as offsite backup.\n# Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed.\n# For shares needing container stops or custom options — add a profile in master.conf.\n HOST2_DAILY_SYNC_SHARES=(\n /mnt/user/Anime_Movies\n /mnt/user/Anime_Shows\n /mnt/user/Books\n /mnt/user/Intros\n /mnt/user/Kids_Movies\n /mnt/user/Kids_Tv_Shows\n /mnt/user/Movies\n /mnt/user/Music\n /mnt/user/Music_Videos\n /mnt/user/stand-up_comedy\n /mnt/user/Sports\n /mnt/user/Tv_Shows\n /mnt/user/Anime_Shows-Old\n /mnt/user/Anime_Movies-Old\n )\n\n# Personal encrypted shares — synced for offsite backup, independent of media shares.\n# ZFS encrypted at dataset level — remote receives encrypted blocks, cannot read content.\n# See README-Rsync_Setup.md for ZFS encryption setup before uncommenting.\n HOST2_PERSONAL_SHARES=(\n # /mnt/user/HOST2-Personal # uncomment after creating encrypted dataset\n )\n\n# ━━━ Weekly Sync Shares ━━━\n# Appdata shares synced during the weekly maintenance window (Sunday 2:30am).\n# Containers stopped both sides before sync — full clean state guaranteed.\n# Profiles drive container stops, excludes, and options — configured in master.conf RSYNC section.\n HOST2_WEEKLY_SYNC_SHARES=(\n # fill in when HOST2 is back online\n # \"/mnt/user/Media_Server/Emby\"\n # \"/mnt/user/appdata-Fallback/Critical-Data\"\n )\n\n# ━━━ Intermediate Sync Shares ━━━\n# Shares synced every 4 hours by intermediate_sync_maintenance.sh.\n# Uses DEFAULT_RSYNC_OPTS (no --delete) — for sub-daily propagation of metadata or watch state.\n# Full media share sync stays in the daily window. Leave empty to skip mid-day rsync entirely.\n HOST2_INTERMEDIATE_SYNC_SHARES=(\n # fill in when HOST2 is back online\n # Example: \"/mnt/user/Emby_Metadata\"\n )\n\n# ━━━ Critical Sync Shares ━━━\n# Appdata shares synced every 30 minutes by critical_sync_maintenance.sh.\n# Format: \"/path/to/share\" or \"/path/to/share|profile-name\"\n HOST2_CRITICAL_SYNC_SHARES=(\n # fill in when HOST2 is back online\n # \"/mnt/user/appdata-Fallback/Critical-Data|critical-fallback\"\n # \"/mnt/user/Media_Server/Emby|emby-fallback\"\n )\n\n# ━━━ Backup Verify ━━━\n# Shares verified by backup_verify.sh — random file checksum comparison against remote.\n# Leave empty to use HOST2_DAILY_SYNC_SHARES automatically.\n# Sample size and minimum file size defined in master.conf.\n HOST2_BACKUP_VERIFY_SHARES=(\n # leave empty to use HOST2_DAILY_SYNC_SHARES automatically\n )\n\n# ━━━ HOST2 Rsync Profile — host2-appdata ━━━\n# HOST2-specific appdata sync profile — extends the shared PROFILE_* arrays in master.conf.\n# Use for appdata unique to HOST2.\n# Shared appdata (auth stack, Emby) use dedicated profiles defined in master.conf.\n# Run manually: bash Rsync/rsync.sh /mnt/user/appdata-Fallback/HOST2-Appdata --profile=host2-appdata\n PROFILE_RSYNC_OPTS[host2-appdata]=\"-av --info=progress2 --bwlimit=${PROFILE_BW_LIMIT[host2-appdata]:-8000}\"\n PROFILE_BW_LIMIT[host2-appdata]=8000\n PROFILE_RETRY_COUNT[host2-appdata]=3\n PROFILE_SLEEP[host2-appdata]=300\n PROFILE_CRITICAL_CONTAINER_NAMES[host2-appdata]=\"\" # fill in when HOST2 is back online\n PROFILE_DELAYED_CONTAINERS[host2-appdata]=\"\"\n PROFILE_CONTAINER_DELAY[host2-appdata]=5\n PROFILE_EXCLUDE_DIRS[host2-appdata]=\"logs *.tmp\"\n\n# ==============================================================================================\n# ── DOCKER ────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Docker Daily Restart ━━━\n# Containers restarted every day via DAILY_MAINTENANCE_SCRIPTS.\n# Fill in when HOST2 is back online — add containers that degrade without daily restart.\n HOST2_DAILY_RESTART_CONTAINERS=(\n \"NginxProxyManager\"\n # add HOST2 daily restart containers here\n )\n\n# ━━━ Docker Weekly Restart ━━━\n# Less critical services restarted weekly via WEEKLY_MAINTENANCE_SCRIPTS (Sunday 2:30am).\n# Containers already stopped for weekly sync — restart adds zero extra downtime.\n HOST2_WEEKLY_RESTART_CONTAINERS=(\n # add HOST2 weekly restart containers here\n )\n\n# ━━━ Docker Watchdog ━━━\n# Per-HOST2 container configuration for docker_watchdog.sh.\n# Shared thresholds and toggles live in master.conf.\n\n# Memory hard limits in MB — immediate restart if exceeded.\n# Set at \"container is clearly broken\" not \"container is busy\".\n# 20GB=20480 16GB=16384 12GB=12288 10GB=10240 8GB=8192 4GB=4096 2GB=2048 1GB=1024\n declare -A HOST2_WATCHDOG_CONTAINERS=(\n [\"Emby\"]=16384 # fill in correct limit when HOST2 is back online\n )\n\n# HTTP health check URLs — checked every cycle, strike system before restart.\n# Only add containers with a meaningful web interface to check.\n declare -A HOST2_WATCHDOG_CONTAINER_URLS=(\n [\"Emby\"]=\"http://localhost:8096\"\n )\n\n# Required containers — must always be running on HOST2.\n# Strike system before restart — repeated failures go on skip list, auto-clears on recovery.\n# Listed in dependency order — dependencies before dependents.\n HOST2_WATCHDOG_REQUIRED_CONTAINERS=(\n \"NginxProxyManager\"\n # add HOST2 required containers here when back online\n )\n\n# Containers to skip in Tier 2 global scan — legitimately stopped or frequently restarting.\n# Watchdog leaves these alone entirely — no restart attempts, no crash loop tracking.\n HOST2_WATCHDOG_SCAN_IGNORE=(\n # add HOST2 scan ignore containers here when back online\n )\n\n# Dependency ordering — skip restarting a container if its dependency is also down.\n# Prevents watchdog from restarting dependent services before their dependencies are up.\n# SPACE-SEPARATED STRINGS — converted to array at runtime.\n declare -A HOST2_WATCHDOG_DEPENDENCIES=(\n # add HOST2 dependencies here when containers are defined\n # [\"Authelia\"]=\"Mariadb-Authelia Redis-Authelia\"\n )\n\n# Per-container appdata growth suppress ceilings in MB.\n# ONLY needed in specific cases — growth rate detection covers all containers automatically.\n# Use when a container legitimately has large stable data and you want to suppress false-positive\n# growth alerts. Add entries here only when a container triggers warnings it shouldn't.\n declare -A HOST2_WATCHDOG_APPDATA_SIZES=(\n # add HOST2 suppress entries here only as needed\n )\n\n# ━━━ Docker Network Connect ━━━\n# Containers connected to custom networks at array start by docker_network_connect.sh.\n# Networks created if they don't exist — idempotent, safe to re-run.\n HOST2_NETWORK_CONNECT_CONTAINERS=(\n # fill in when HOST2 is back online\n )\n\n HOST2_NETWORK_CONNECT_NETWORKS=(\n # fill in when HOST2 is back online\n )\n\n# ==============================================================================================\n# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ DDNS ━━━\n# DDNS containers HOST2 manages — started/stopped by fallback.sh per DDNS absolute rules:\n# Internet loss → stop immediately\n# Failover → HOST1 starts HOST2's DDNS as Tier 1 (before any other containers)\n# Handback → stop HOST2's DDNS on HOST1 → rsync → start containers → start local DDNS last\n HOST2_DDNS_CONTAINERS=(\n \"Gmer4Lfe.us\"\n )\n\n# ━━━ Internet Loss ━━━\n# Containers stopped immediately on HOST2 when internet connection is lost.\n# Prevents external-facing services from operating without connectivity.\n FALLBACK_HOST2_STOP_ON_NO_NET=(\n \"Gmer4Lfe.us\"\n )\n\n# ━━━ Fallback Tiers — HOST2 Runs for HOST1 ━━━\n# Containers HOST2 starts when HOST1 goes down.\n# Tier 1 is always immediate — vital services cannot wait.\n# Higher tiers activate after HOST1_TIER*_DELAY minutes (set in host1.conf).\n FALLBACK_HOST2_COVERS_HOST1_TIER1=(\n \"Gmer4Lfe.com\"\n \"Gitea\" # source of truth — must be reachable even when HOST1 auth stack is down\n \"Emby\"\n \"VaultWarden-Gmer4Lfe\"\n \"Dispatcharr\"\n \"Dispatcharr-Basic\"\n \"Dispatcharr-Iptv-Users\"\n \"ErsatzTV-Emby\"\n )\n\n FALLBACK_HOST2_COVERS_HOST1_TIER2=(\n \"Postgres-NextCloud\"\n \"NextCloud\"\n \"PostgreSQL_Immich\"\n \"Immich-Gmer4Lfe\"\n )\n\n FALLBACK_HOST2_COVERS_HOST1_TIER3=(\n \"Gitea\"\n )\n\n FALLBACK_HOST2_COVERS_HOST1_TIER4=(\n \"Sonarr\"\n \"Radarr\"\n \"Lidarr\"\n \"Readarr\"\n \"Prowlarr\"\n \"Bazarr\"\n \"SABnzbd-Gmer4Lfe\"\n \"Qbittorrent-Gmer4Lfe\"\n \"LidaTube\"\n \"Pinchflat\"\n \"ChannelTube\"\n )\n\n# ━━━ Tier Delays — HOST2's Containers on HOST1 ━━━\n# How long HOST2 must be down before each tier activates on HOST1 — in minutes.\n# Tier 1 is always immediate — no delay var needed.\n HOST2_TIER2_DELAY=240 # 4 hours — productivity services\n HOST2_TIER3_DELAY=720 # 12 hours — secondary services\n HOST2_TIER4_DELAY=1440 # 24 hours — arrs + downloaders\n\n# ━━━ Rsync Writeback — HOST2 Appdata Back on Handback ━━━\n# Syncs HOST2 appdata BACK to HOST2 when it comes back online after a failover.\n# Containers stopped before writeback — clean source, no competing writes.\n#\n# HOST2_TIER1_WRITEBACK_DELAY: short outages skip Tier 1 writeback — primary state\n# is more reliable than dirty sync data for brief outages.\n HOST2_TIER1_WRITEBACK_DELAY=60 # skip writeback if outage under 1hr\n\n# Tier 4 automatically syncs HOST2_DAILY_SYNC_SHARES — only list paths NOT in that array.\n FALLBACK_HOST2_WRITEBACK_TIER1=(\n # \"/mnt/user/appdata-Fallback/Jayred365-Emby\"\n )\n\n FALLBACK_HOST2_WRITEBACK_TIER2=(\n # \"/mnt/user/appdata-Fallback/Jayred365-Important\"\n )\n\n FALLBACK_HOST2_WRITEBACK_TIER3=(\n # \"location-placeholder\"\n )\n\n FALLBACK_HOST2_WRITEBACK_TIER4=(\n \"/mnt/user/appdata-Fallback/Arrs_Stack\" # arr databases — downloads queued during outage\n )\n\n# ==============================================================================================\n# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Media Permissions ━━━\n# Shares that media_shares_permissions.sh applies PERMISSIONS_MODE and PERMISSIONS_OWNER to.\n# Runs first in DAILY_MAINTENANCE_SCRIPTS — arr cleanup depends on correct ownership.\n HOST2_MEDIA_PERMISSION_SHARES=(\n /mnt/user/Anime_Movies\n /mnt/user/Anime_Shows\n )\n\n# ━━━ Media Cleaner ━━━\n# Folder lists for media_cleaner.sh — two profiles: anime and media.\n# File patterns shared across all servers — defined in master.conf.\n# Called via DAILY_MAINTENANCE_SCRIPTS. Run manually: Media/media_cleaner.sh anime|media\n HOST2_ANIME_CLEAN_FOLDERS=(\n /mnt/user/Anime_Movies\n /mnt/user/Anime_Shows\n )\n\n HOST2_MEDIA_CLEAN_FOLDERS=(\n # fill in when HOST2 is back online\n )\n\n# ==============================================================================================\n# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Certificate Monitor ━━━\n# Domains checked via direct openssl connection — not relying on NPM's certificate state.\n# Checks the actual certificate served, not what NPM thinks it has.\n# Thresholds (CERT_WARN_DAYS, CERT_CRIT_DAYS) defined in master.conf.\n HOST2_CERT_MONITOR_DOMAINS=(\n # fill in when HOST2 is back online\n )\n\n# ━━━ SMART Health ━━━\n# Drives skipped in SMART attribute monitoring — hardware is server-specific.\n# Thresholds read from dynamix.cfg at runtime — fallbacks in master.conf.\n HOST2_SMART_IGNORE_DRIVES=(\n \"sda\" # boot USB — SMART not meaningful on flash drives\n )\n\n# ━━━ ZFS Report ━━━\n# Pools excluded from the weekly ZFS health report — reduces noise from single-disk array pools.\n# Pool health thresholds defined in master.conf.\n HOST2_ZFS_REPORT_IGNORE_POOLS=(\n # fill in when HOST2 is back online\n )\n\n# ==============================================================================================\n# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# Ramdisk size ceiling — tmpfs only uses RAM actually needed, not the full size upfront.\n# Adjust HOST2_RAMDISK_WARN_GB and HOST2_RAMDISK_LOW_GB together if this changes.\n HOST2_RAMDISK_SIZE=\"8G\"\n\n# Usage thresholds — coupled to HOST2_RAMDISK_SIZE, adjust all three together if size changes.\n# Hysteresis gap (6.8 - 5.5 = 1.3GB) prevents flip-flop between ramdisk and SSD.\n HOST2_RAMDISK_WARN_GB=6.8 # flip to SSD when ramdisk usage reaches this\n HOST2_RAMDISK_LOW_GB=5.5 # flip back to ramdisk when usage drops to this\n\n# SSD fallback path — where transcodes land when ramdisk exceeds HOST2_RAMDISK_WARN_GB.\n# Must be on cache pool — array disks too slow for active transcode writes.\n HOST2_TRANSCODE_SSD=\"/mnt/cache/Temp_Storage/Emby/Transcodes/\"\n\n# Media servers sharing the ramdisk transcode space on HOST2.\n# Format: \"ContainerName|URL|APIKey|Type\" — Type: emby | jellyfin | plex\n# Entries with placeholder API keys are skipped automatically.\n# ⚠️ Tdarr does NOT belong here — keep Tdarr on SSD, not ramdisk.\n HOST2_TRANSCODE_SERVERS=(\n \"${HOST2_EMBY_CONTAINER}|${HOST2_EMBY_URL}|${HOST2_EMBY_API_KEY}|emby\"\n \"${HOST2_JELLYFIN_CONTAINER}|${HOST2_JELLYFIN_URL}|${HOST2_JELLYFIN_API_KEY}|jellyfin\"\n )\n\n# ==============================================================================================\n# ── ARR STACK ─────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Used by arr cleanup scripts and arrs_failed_stalled_recovery.sh.\n# detect_hosts() selects HOST2 vars when running on HOST2.\n# Lidarr does not run on HOST2 — HOST1_LIDARR_RECOVERY flag handles the exit cleanly.\n#\n# PATH MAPS — container path → host path translation.\n# Arr stores file paths using container-internal paths — scripts need host paths to scan.\n# Add one entry per root folder in arr Settings → Media Management → Root Folders.\n HOST2_FANART_API_KEY=\"Yd7147a43b692df0b364b94dc47efb81\"\n HOST2_LASTFM_API_KEY=\"be6dc169c33ae263e690c30d18b7491d\"\n\n# ━━━ Sonarr ━━━\n HOST2_SONARR_URL=\"http://localhost:8989\"\n HOST2_SONARR_API_KEY=\"130decd3db5b4c25afad64864cd03f9f\"\n HOST2_SONARR_TV_ROOT=\"/mnt/user/Anime_Shows\"\n\n declare -A HOST2_SONARR_PATH_MAP=(\n # fill in when HOST2 is back online\n # [\"/tv\"]=\"/mnt/user/Anime_Shows\"\n )\n\n# ━━━ Radarr ━━━\n HOST2_RADARR_URL=\"http://localhost:7878\"\n HOST2_RADARR_API_KEY=\"d43a3ec6cf1549edb4af0cc63f98b2a9\"\n HOST2_RADARR_MOVIES_ROOT=\"/mnt/user/Anime_Movies\"\n\n declare -A HOST2_RADARR_PATH_MAP=(\n # fill in when HOST2 is back online\n # [\"/anime-movies\"]=\"/mnt/user/Anime_Movies\"\n )\n\n# ━━━ Arr Recovery Toggles ━━━\n# false = skip that arr on this host — exits cleanly without error\n HOST2_SONARR_RECOVERY=true\n HOST2_RADARR_RECOVERY=true\n # HOST2_LIDARR_RECOVERY not set — Lidarr does not run on HOST2\n\n# ==============================================================================================\n# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Per-host check toggles and NIC config for system_watchdog.sh.\n# Aliased by detect_hosts() — script uses unprefixed SYS_WATCHDOG_* names.\n# HOST2: i5 10th gen 64GB — being rebuilt, lighter workload, no ZFS cache pools.\n#\n# Conservative defaults during rebuild — re-enable checks as HOST2 stabilises.\n# Three-tier response — all critical checks enabled regardless of rebuild state:\n# Tier 1 (bypass strikes, reboot now): docker daemon, rootfs full, kernel oops, FD, /boot\n# Tier 2 (bypass strikes with OOM): RAM critical + OOM kills in cycle\n# Tier 3 (standard strike system): selectively disabled during rebuild\n#\n# RAM tiers, OOM limits, and reboot loop settings in master.conf System Watchdog section.\n\n# ━━━ Primary NIC ━━━\n# Network interface for NIC state check — verify with: ip link show | grep \"^[0-9]\"\n# Common values: eth0, bond0, br0, eno1\n HOST2_SYS_WATCHDOG_NIC=\"eth0\"\n\n# ━━━ Tier 1 — Critical Checks ━━━\n# All critical checks always enabled — these protect against acute failure regardless of\n# rebuild state. Disabling any is not recommended.\n\n# Docker daemon unresponsive → try restart, reboot if restart fails.\n HOST2_SYS_WATCHDOG_CHECK_DOCKER_DAEMON=true\n\n# rootfs at critical threshold (ROOTFS_CRITICAL_PCT=99) → reboot immediately.\n HOST2_SYS_WATCHDOG_CHECK_ROOTFS=true\n\n# Kernel BUG/Oops in dmesg delta since last cycle → reboot immediately.\n HOST2_SYS_WATCHDOG_CHECK_KERNEL_OOPS=true\n\n# File descriptor exhaustion at FD_CRITICAL_PCT (95%) → reboot immediately.\n HOST2_SYS_WATCHDOG_CHECK_FD=true\n\n# /boot read-only detected → reboot immediately.\n HOST2_SYS_WATCHDOG_CHECK_BOOT=true\n\n# ━━━ Tier 2 — Urgent OOM Check ━━━\n# Both must be enabled for Tier 2 bypass to function.\n\n# Track kernel OOM kills each cycle via /proc/vmstat oom_kill delta.\n HOST2_SYS_WATCHDOG_CHECK_OOM=true\n\n# Free RAM check — 64GB RAM on HOST2, tiers adjusted relative to HOST1.\n# Update master.conf SYS_WATCHDOG_MEM_* thresholds if HOST2 needs different values.\n# Currently inheriting shared master.conf values — may want lower thresholds on 64GB.\n HOST2_SYS_WATCHDOG_CHECK_RAM=true\n\n# ━━━ Tier 3 — Standard Checks (strike system) ━━━\n# Several checks disabled during rebuild — enable progressively as HOST2 stabilises.\n# Each check must fail SYS_WATCHDOG_STRIKE_LIMIT consecutive cycles before action.\n\n# /var/log filesystem usage above SYS_WATCHDOG_LOG_PCT.\n HOST2_SYS_WATCHDOG_CHECK_LOG=true\n\n# ZFS ARC memory check.\n# DISABLED — HOST2 has no ZFS cache pools. Enable if ZFS pools are added later.\n HOST2_SYS_WATCHDOG_CHECK_ARC=false\n\n# CPU temperature above SYS_WATCHDOG_CPU_TEMP_MAX (95°C).\n HOST2_SYS_WATCHDOG_CHECK_CPU_TEMP=true\n\n# Load average above SYS_WATCHDOG_LOAD_MULTIPLIER × core count.\n# DISABLED — rebuild operations cause legitimate load spikes. Enable after rebuild.\n HOST2_SYS_WATCHDOG_CHECK_LOAD=false\n\n# Zombie process count above SYS_WATCHDOG_ZOMBIE_LIMIT (50).\n HOST2_SYS_WATCHDOG_CHECK_ZOMBIES=true\n\n# docker_watchdog.sh persistent skip list check.\n# DISABLED during rebuild — skip list may be unreliable mid-rebuild, avoid false reboots.\n# Enable once HOST2 is fully operational and docker_watchdog.sh is running stably.\n HOST2_SYS_WATCHDOG_CHECK_CONTAINERS=false\n\n# /tmp filesystem usage with auto-clear attempt.\n HOST2_SYS_WATCHDOG_CHECK_TMP=true\n\n# Array disk error count delta in /proc/mdstat.\n HOST2_SYS_WATCHDOG_CHECK_MDSTAT=true\n\n# Primary NIC operstate — uses HOST2_SYS_WATCHDOG_NIC above.\n HOST2_SYS_WATCHDOG_CHECK_NETWORK=true\n\n# sshd running check — restart attempt before escalating.\n HOST2_SYS_WATCHDOG_CHECK_SSHD=true\n\n# Runaway process detection.\n# DISABLED — rebuild workloads may legitimately peg CPU. Enable after rebuild.\n HOST2_SYS_WATCHDOG_CHECK_RUNAWAY=false\n\n# ==============================================================================================\n# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Containers to manage under pressure — see master.conf RW_CRITICAL_CONTAINERS for exclusions.\n\n# docker pause at medium pressure (RAM < RW_RAM_MEDIUM_GB or load > medium threshold)\n# Suspended in-place — instant to pause/unpause, no state lost, no restart delay.\n HOST2_RW_PAUSE_CONTAINERS=(\n # fill in when HOST2 is back online\n )\n\n# docker stop at hard pressure (RAM < RW_RAM_HARD_GB)\n# Full stop — these are optional/heavy services that free significant RAM when stopped.\n# resource_watchdog.sh restarts them when pressure fully clears (RAM >= RW_RAM_RECOVER_GB).\n HOST2_RW_STOP_CONTAINERS=(\n # fill in when HOST2 is back online\n )\n\n# ==============================================================================================\n# ── AUTH STACK ────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Credentials for the Varaverk Auth Stack page (NPM, lldap, Authelia).\n# Fill in when HOST2 is back online.\n\n# ━━━ NginxProxyManager ━━━\n HOST2_NPM_URL=\"http://localhost:81\"\n HOST2_NPM_USER=\"\" # NPM admin email\n HOST2_NPM_PASS=\"\" # NPM admin password\n\n# ━━━ lldap ━━━\n HOST2_LLDAP_URL=\"http://localhost:17170\"\n HOST2_LLDAP_USER=\"admin\" # lldap admin username\n HOST2_LLDAP_PASS=\"\" # lldap admin password\n\n# ━━━ Authelia ━━━\n HOST2_AUTHELIA_CONFIG=\"/mnt/user/appdata-Fallback/Critical-Data/Authelia/configuration.yml\"\n HOST2_AUTHELIA_CONTAINER=\"Authelia\"\n\n# ==============================================================================================\n# ──────────────────────── End Of HOST2 Variables ──────────────────────────────────────────────\n# ==============================================================================================","numLines":664,"startLine":1,"totalLines":664}},"sourceToolAssistantUUID":"9baff7e3-2fc9-40a8-8064-4243f238149c","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"last-prompt","lastPrompt":"NPM auth failed — check credentials in host conf","leafUuid":"8478fe05-f414-45a6-b9e8-8bae17c592fb","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"ai-title","aiTitle":"Troubleshoot NPM authentication failure","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"mode","mode":"normal","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"permission-mode","permissionMode":"default","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"parentUuid":"8478fe05-f414-45a6-b9e8-8bae17c592fb","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01LNWAJ6hTGc4iqbkRmksFPa","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"Eq4PCmUIDhgCKkA94N3AUnOqc/DaaYGHPuU3kA6tVFDrt8MDPD9RP/TPHRWpEqWaPUE1qn53LUPPR8/7/dGMrqYQfeM3Y/VdU6kiMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIMg6MwnkaZL7JekQtsGgyIK0PpYdmTUVjf2yAiMNmHPKqktZw5V0PbkDrJQyUmm9JrYtZE8nvgxNNPZtOsiRkEJvgKfzSxH9jBxs3y/Sr2DVYHXPuk5iMHV9qz3azEVWrWD5T5taToTmZpgsP3muEg44zVMwEGt/ewbLMElxMa7ItVb3kakuoSaTAqqKyQwXKEIzUm/vOd/5nS9pHuBrdert/KjRMG7xllB1SgZsRoxEb4gdfrQe9qvRptrmiRwXrradvIUkE3q++htyF9+pB25w3ZXiDbS9irdww6S+VMqnXYXkM1Ttf1/TLUc3l4HYQOlpmXXBnv92uowGlT2h537En4Cc0/bQhT1YNDkm3c1RUvwl0nnYa3MEdCnnHgJ15co2kHtImHj2YikKZR+fdQEqjVxqgnR+UukNqc3eqUF7ZF4pGt3dbhdFg7cnFzp7SPWYj1L2xQLxQzo7oKn8RcEKVjbCph/Ne68a/qNLyRbEiq0avOBF+qr9EqTc8Axmg59IREI09TxrNK52QtHFsJYylGGFgrVovHcRKPDJb0qSR5XSys1oy6f3YDp8eSlAG0Zi0H+yJU7vEFc3kOevjxW32sWcx8ZWFtlMgo5oEyEp+EHzZNJPD6bZ/iRxt33AafERBpnVHaSb3eWpGHX9ny/UXYURUplXuk0p3fZBgnc4BmHff2MJmTxx0Gmp1sRI9OOpeaIj2OdRyrxh9TCsVuYDav3JxNqntlJwL5mxNWlHyE8cmaD75L4QB5bWsqleaI9A0rrliroDYGb79vQfAM9jvKBClN4S0B8V5FFasOF4lxrlCaFFdLKu8eaWSLegaSQiSPoI1iS/eC+hZedZ+eczUaIG7ouBJWi7yZEacns1h8xFv246cfHsObV2LDpr/6vVOrJX7ZxS+UxeSGw18jUSo9qNIjSkpsbhpHaTwGCDMS/w+C/54KB0LVQ+p3D56g7GREcoHjo6BYIMf2vM/Bg+c7Q0f3UIfjq+lTmTQb2RtRz7TEJE4Bxn0it83TjkUitTO+mETutCKZwMk15ZmTtO22VMznzshNowEFBRo55umGD+n3S40mUroiWr5VIg1d2qt1u9Ys7ig+9AJInHDxdBo/zRc0btQMdPP4Ooy7T1EfrevuFK91QBlx1KzsPF645b9XlfT2wEKB/6pPbV7Fd/zwb6wmOYiaTXXDayykI1OR6RDNbeO494zIO7OoiUo1sOXTeZmdTfFhlDWxn7THYXc0+lvsmsRIF5LiqUZoywXVxbYlDkdKp0fbRz5dcDzNyzq7mDx8ciATniJZlvyEbKB614hY/D4QgFTixXsde/wQk2M1oNAHn5mo4w/DoZq7TXn6duBsg0FRl5jUdiGIPL8o6bz8x5H7GmcVESk84YB7lOfekmyqtV4PQbGk8RCWfSTbw8JvI/KwbjArS5FqD0xUqVxnAEtEcubcLD5MtLAM9V3fXQc73TUl9wnLhVcVc1KZ/54msXoykbS/6yiwQ80VSGlMetcuaAINyQERQYJY0joLqnbcCLY9Rl0voc70RDJRiJRRbHHqroXKsT1/m8/LbCZVfXE+nrmJwvqymV1Y+ctjIrFyMCWmKgZCw6Y2yNCYfqEeDo5Av9USRsFW2nOiQyFnztp1DC+0iFQm+fqqCaiiYqfdee71hAPD/EN/bdv06YCpmvofgE2wxvT61iuBY3+YufJ0v/QmrbJYg0LVFz5Z/jWdWZH+A/ZdGL6TIKxhJ4emZlUiagGILyi++aRcwa5Gc1MDKLZ7nBSfIyo2C+UHEwhQ357K4VwE+7kmEli+0vghouGJ6+PRWNNhL7QlqLu7FbloBn5iCI/B/N9hw2w7bEHvQd1vExLQ2QuaDHjAgks1h7odku+Dt0f25KTD8syqSNu5FhmIo+Lcg3z1ppjS5AT5boybCkqmzPULq6wGSl3z0SSSD2XytRMsrWuEcZyPtZZz1IPeIJ1+3fDOH0bnp3pop4mJu1l0sMIh4XXjpO0y7e3YJlOmKrkIi8cjv2CxStGTaCpS7ThBA0dAFA1W06vZL8PS81OU2CKriiLj6OGGXXY4wIshkggsCI7jTu5YzIt/cZ+oezi2VTlThfed5UYWH8/vKlN71tbzJooGBQc+C2kBWzEYkAxS7K+a3WPPavfk0H3m/j8hqLsQP1Ix2cqu6RBZAJo8lGymD6hD/JLVnlPQ7ZKsmptbrqF4E1Hk/spwffRrSfKrbdvwWu/Ki2NVAbwgcio0RMuZ6r1ud7oWBKcNWwQ+pJOEe+83HTeduTNLRKqGvwJGCXgTIUpJSO5HETGDn2kVYzRpO8aVGEqzws1AdARyz1jkKCGVzV69QM7e5QSBZs6CDwSii1Vril8U6THsRxv81DZsLOzTY3WX5Hi4OK97k/749WWA20f8KEpHq2kFus0Ez+OGoqCGBlFjsv7RxiQsmXk5TdiJm+ayUeOsYSlqqLXsfvz+gAXgTykwsddyHY7XFSQEnsnVmhgB"}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":24215,"cache_read_input_tokens":25439,"output_tokens":679,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":24215,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":679,"cache_read_input_tokens":25439,"cache_creation_input_tokens":24215,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":24215},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRkW87EoLWdADXmT9gM","type":"assistant","uuid":"59b739d5-2581-4fa0-a337-d57084c8c52f","timestamp":"2026-06-06T17:49:05.294Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"59b739d5-2581-4fa0-a337-d57084c8c52f","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01LNWAJ6hTGc4iqbkRmksFPa","type":"message","role":"assistant","content":[{"type":"text","text":"Let me check what code uses NPM credentials."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":24215,"cache_read_input_tokens":25439,"output_tokens":679,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":24215,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":679,"cache_read_input_tokens":25439,"cache_creation_input_tokens":24215,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":24215},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRkW87EoLWdADXmT9gM","type":"assistant","uuid":"543c16fc-7bb5-4f56-b93c-8724a66b729e","timestamp":"2026-06-06T17:49:05.294Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"543c16fc-7bb5-4f56-b93c-8724a66b729e","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01LNWAJ6hTGc4iqbkRmksFPa","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Xk3dZZHxeeEhNKgPuZTo2P","name":"Bash","input":{"command":"grep -rn \"NPM_USER\\|NPM_PASS\\|NPM_URL\" /boot/config/plugins/varaverk --include=\"*.sh\" --include=\"*.php\" | grep -v \".git\"","description":"Find all NPM credential references in scripts"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":24215,"cache_read_input_tokens":25439,"output_tokens":679,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":24215,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":679,"cache_read_input_tokens":25439,"cache_creation_input_tokens":24215,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":24215},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRkW87EoLWdADXmT9gM","type":"assistant","uuid":"9e6a8c4e-9bae-49af-9e04-74e4f0716f6d","timestamp":"2026-06-06T17:49:06.639Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"9e6a8c4e-9bae-49af-9e04-74e4f0716f6d","isSidechain":false,"promptId":"0a108bc8-cb46-4a98-91cc-27f1cab8a949","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Xk3dZZHxeeEhNKgPuZTo2P","type":"tool_result","content":"/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:70:# HOST*_NETWORK_WATCHDOG_NPM_URL external URL to test full proxy stack\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:101:_npm_url_var=\"${MY_ID}_NETWORK_WATCHDOG_NPM_URL\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:105:NPM_URL=\"${!_npm_url_var:-}\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:128:log \"$ICON_NET DDNS: ${DDNS_DOMAIN:-not configured} → ${DDNS_CONTAINER:-no container} NPM: ${NPM_URL:-not configured}\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:154: echo \"$ICON_GEAR NPM URL: ${NPM_URL:-not configured}\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:188: if [[ -n \"$NPM_URL\" ]]; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:189: if curl -sf --max-time \"${NETWORK_WATCHDOG_NPM_TIMEOUT:-10}\" \"$NPM_URL\" >/dev/null 2>&1; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:190: echo \" $ICON_SUCCESS NPM proxy: reachable ($NPM_URL)\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:192: echo \" $ICON_ERROR NPM proxy: NOT reachable ($NPM_URL)\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:272:if [[ -n \"$NPM_URL\" ]]; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:276: if curl -sf --max-time \"${NETWORK_WATCHDOG_NPM_TIMEOUT:-10}\" \"$NPM_URL\" >/dev/null 2>&1; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:277: log \"$ICON_SUCCESS NPM proxy reachable — $NPM_URL\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:288: warn \"$ICON_ERROR NPM proxy not reachable — $NPM_URL (strike $NPM_STRIKES/$NPM_STRIKE_LIMIT)\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:289: notify \"NPM proxy not reachable on $(hostname) ($MY_ID) — $NPM_URL (strike $NPM_STRIKES/$NPM_STRIKE_LIMIT)\" \\\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:299: notify \"NPM proxy restarted on $(hostname) ($MY_ID) — $NPM_URL was unreachable for $NPM_STRIKES cycles\" \\\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:10: 'npm_url' => rtrim($v[\"{$host}_NPM_URL\"] ?? 'http://localhost:81', '/'),\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:11: 'npm_user' => $v[\"{$host}_NPM_USER\"] ?? '',\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:12: 'npm_pass' => $v[\"{$host}_NPM_PASS\"] ?? '',\n/boot/config/plugins/varaverk/Plugin/unraid/include/watchdog.php:300: 'npm_url' => vv_wd_scalar($raw, \"{$id}_NETWORK_WATCHDOG_NPM_URL\"),","is_error":false}]},"uuid":"339f5a48-a8a4-4cb1-aa1a-7ab9f143f16b","timestamp":"2026-06-06T17:49:07.189Z","toolUseResult":{"stdout":"/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:70:# HOST*_NETWORK_WATCHDOG_NPM_URL external URL to test full proxy stack\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:101:_npm_url_var=\"${MY_ID}_NETWORK_WATCHDOG_NPM_URL\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:105:NPM_URL=\"${!_npm_url_var:-}\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:128:log \"$ICON_NET DDNS: ${DDNS_DOMAIN:-not configured} → ${DDNS_CONTAINER:-no container} NPM: ${NPM_URL:-not configured}\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:154: echo \"$ICON_GEAR NPM URL: ${NPM_URL:-not configured}\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:188: if [[ -n \"$NPM_URL\" ]]; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:189: if curl -sf --max-time \"${NETWORK_WATCHDOG_NPM_TIMEOUT:-10}\" \"$NPM_URL\" >/dev/null 2>&1; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:190: echo \" $ICON_SUCCESS NPM proxy: reachable ($NPM_URL)\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:192: echo \" $ICON_ERROR NPM proxy: NOT reachable ($NPM_URL)\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:272:if [[ -n \"$NPM_URL\" ]]; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:276: if curl -sf --max-time \"${NETWORK_WATCHDOG_NPM_TIMEOUT:-10}\" \"$NPM_URL\" >/dev/null 2>&1; then\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:277: log \"$ICON_SUCCESS NPM proxy reachable — $NPM_URL\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:288: warn \"$ICON_ERROR NPM proxy not reachable — $NPM_URL (strike $NPM_STRIKES/$NPM_STRIKE_LIMIT)\"\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:289: notify \"NPM proxy not reachable on $(hostname) ($MY_ID) — $NPM_URL (strike $NPM_STRIKES/$NPM_STRIKE_LIMIT)\" \\\n/boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh:299: notify \"NPM proxy restarted on $(hostname) ($MY_ID) — $NPM_URL was unreachable for $NPM_STRIKES cycles\" \\\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:10: 'npm_url' => rtrim($v[\"{$host}_NPM_URL\"] ?? 'http://localhost:81', '/'),\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:11: 'npm_user' => $v[\"{$host}_NPM_USER\"] ?? '',\n/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:12: 'npm_pass' => $v[\"{$host}_NPM_PASS\"] ?? '',\n/boot/config/plugins/varaverk/Plugin/unraid/include/watchdog.php:300: 'npm_url' => vv_wd_scalar($raw, \"{$id}_NETWORK_WATCHDOG_NPM_URL\"),","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"9e6a8c4e-9bae-49af-9e04-74e4f0716f6d","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"339f5a48-a8a4-4cb1-aa1a-7ab9f143f16b","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01Hc6i3QpQ5WvfRYYS7qbVDF","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"EoQZCmUIDhgCKkBaRTIKd0yBsIgiAox4mVgALOiMWYF8b8gvJXTc++kVXLKo1Uky8Skwopxu6I99gfR98F8J9foyyhQeYOWAm+8gMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIM6GGt0ZDicpDRJlqRGgzQ8MX0RDPmEi4StIAiMDGYzyFx4Lb9uEbeY7OIijPBtoADrLn7v8pO2WBVzgnlBMfmoFFZr986QXlqFDl2eSrMF4ZvkgG/h8iWJqCxs3aZj/yNBqxugMAmJIjLod1SGjR4Mp1Uf+7LHyigKk/qeme+Uo0ehslqz0Pt2mR0xDggtYsb1/PlGrWvdWoUv5IBKHjbXCSu6zGYvgh/2fTJ8myAjxyYOT6SHQVJ4IIjr5TRJUsjUoG49jJ9fajzeHC0KHzWLI/We3LCrwXmFshz4H/jGohNGFhO6YCTpoxeicFaZCTFNrhZUhdu/uEn4oQxW0HQgXFvHJLnVY0OGfQDzLAAeiTW33S/OsRGI7eVEFE31y1Tv0i0Im+VT6oZkmjpLf2g3IZDqsn6H96uRAycJG2iqEn5rHXVML5YR0/P6GDKoG35/jYhX1SkCvJ5rCXI0FC5qx2FiXuq8m9qULl4ltElh183fZrOhqwgOFnsWX8ALboS+wVB9z+++vFdulSpktVTGI80xdzUKmsJTpH9UWRuHuJ/wLo9/SAGIbEHochO7zU9hzCWtplrVFURddPc4Y+Dn5aFfFufMBDQg9IJw3bKpUWEqD7NMbEPtvjuMn8LqReY0hWPWLBOJntB6zDq7gwoGnDfsBWlFeUOqBZoP4JtLl0M/BdMC1l9wEA++rwLxFAphjWUIIWDsiud8LqBtGEdZUBUIbbs8c6QbA0esy3+iyMGpDpoTbvXjtYzebv3FyibPmhRGymd7Uolsi/rRnONvXMSs2hmRQlmv6+CCCT5wjsU2lXQVQpV8xx8ZXOWk1ak3WlQIrBA8h1DKEg0e6zd0z992cBq6Llu+Pjl/yh6kiQ69F8YCwi/kGfmtaoc0bBhTzBSwrecU4tzm89888xG7PjccmRAF8CK71AgvYLuCaWas2qorNbua7YESQ3RUvb+TWpLZFb2Via+F5yJpLE27jygP8GXH3aLQ3/ZmRrgV/lyQzVpbKnbCpW/U4DglWg/AGoPJVbO1He+noffVbpM9w52OfDW0SrQR7l+1gr0+eJnorwafNmM4iE/ydNefjMSwrbXk77bZxthWFck0Rs1Id3qkf8bwQ2NPtKd/0mVN6SE/eexNfxykHlKdw+/xxV/7y8eeGvd6LV1kBW3L8Eo2RxuDmVD3EMZygfy5LjtuXDLHmqlxJWwPiYIM9BjyAaSBBpAZgubgihjLDsKgced5Vq7e7pe/C22IjWrE9t49Hx9WT9RYONQcIOki2aP3e7JgPjfR4hav4hJWcBdzn0crHf+t21lDqkFUtuspJSLuLfIXfZMI9sJ+OIWjxf2Z19BXi0F8Fr+o9JGq3SV+AwDAKx4aa2JosLvtWweUTrwQKrlkHYyozole0uRspQNtdGJaq5vr/+B5Acj8QPvTD7sNxmDrTpv+p8SR+JkDvUiNLgMHpRwQQ8n+8vRhAyCTIo+58GOO7c1Lp+13RHA4M/6EyImv0NhqzfpiaMI9Bbtpv6oOcBMU0I2doI26QDWDhasPhz+jHgINTuhQ6mRwHdIfPeS3dmQY81AV+sxPAHdUF1DYfqCFGeIsq9B885wUDhWTJMIBByTuYR4om9kPHfwuLuvTtmRO1ZJulQSCIoQVPbF4G3r1KSKjU9FFogSTQ7TNhQPUXN72FAcnxTAJQ4J4Tt0pHTGLxSDSjcDT70DsNWl1vAIK8dTwc1hdAsCz3hvcJ8Pd8CDkiv9K5zzzFCGs6TPTQEbRsxNa4Slh51/nrwYZQ/B6mQkPs5pCvZTq6TOPAXuzA2BFcH/Q3IoxD+9pTekpLUpj+jyDA9Te8b7OcIwczlgckeG/6TRNFlpJ6RrQtMQTC7WiDBVZ0a+QA1dA1LVHH7BnHgxynQEBYTxp0TiqNOqHUXqMWcd0b4bL9fHGJ7tgTtJXUxMRQ2F0TwcA1vJl7PAuUXD7n8U50fUPw/Zu9GtXDXePVNsXzUlApT4Bz0LLA0IubPnSkJi5yi7Hd0ciVuV1dW0+ek4elpB2i8OLX1H7ffpV9zFnT2HA18Q8FK0QsmfmZRuJwLprfl+8Ve5Ds5seFxJHYiBj8sBIyGuSYoSXTiN9qknS75wSBgK/4bjxz5WCG0KCnl6TcnbBhroNg7RxqxzCpA9FVTTU61lpWIHflArpzUyVAxOH0wRgTIO7YeU6FC0DPv1PPYAxBKPdMX2Y15/czI2JKc4NagUuueVdSwobJlB2eXkuO7gFy+TkdqlJtpOUyrYfVGE3tWS05kaEA7rKTXbUa8cWZ8jgf/9yubreacZvtv39jElMeOBPDwhDXl2VfbvdyM4KSHmcWxsC+M5lHUlOyIQ9O8d3ymv/R0VObvqOLtBZB31zcL3ClJqtWNSHe5BeMBaMufUh7Y3SJrImLT7E+AnLLMpTFHlcrgvLXmUMpKhs8/Ney8z7GrWXUPPBgQrnA//pKldJAnxBQJ2PgsXvwkf+GFle+93HUcDkniiV25S/IriykqLVG/gFykjhv28RBk58XJQR2LVqWMfw0aVg4PgZythRlRuT0+5KXs25J2YWy+H/RTZu/4CnI5ePO/sVIH6RBd2+neFEtYAOgUM0O8eg/T+2F7FwRaZ8vJ8acXym+P1OwLxiMGTh03FOH52GVZG5p/UTlRj5ZApPpDHVaW/CIu9Yjr/Ua6TsolVDE7u3XPlCrjI3GRyK78RjzsuQc9KB8iWJ1LDHYW7hVkxRb61dNoK4Zop/B1TwTLj2fubfr5ObjNLXjo2fryMVzsJC961FCOTaZW4UQy0/DK4PjQeErdDTCy+Yrhni8Ky9V/B8u2EXVOotez2Yq8GxmQkByPRgRkDCpJAxSScYMuxIJYXnk4h9lmPhNpPJIR9S+4rVo/s1SQHKWnfOW24/GXK9YSzQdKI+TV1ItY1GojrPqmRb4OeSLEtjpbr+wBNUMjg91sY8ndtqEIyEKW0Mn+SKSBVLcwAYaF6qyeC0sBfzrXIMe9PHwjmqgIlcgduw8f+BdO1eX8UqK/+xxTtYA+sueRXo+HcuNp2u2QyfimOc182kI1wQmGaiJ6txAy2A0gl7bBh6PlXvXza6EF+8C1/sn8gXzw3MxlIX/YBAERN686pwACENfN9/TOt/e+DgJOqAZDjSHACL/+COsvZQd9LafSWDRFj8W026jk2QRffc3E599E2VlxhWNRIsuba6D3QDe+EgM43GgVNyrK+BTEuvRFOm2FRqypUbjkIp5QUcswm3ijxzG5nDehiSqiynZjbTVLhMNuxxcamZ+gZHaH3vlQ5jqyckPErxQl9sGL1+Q1dHfLuyEj6Vno3iYL9p+ZsWe9Mrwx1X828WDkGDwHlOuMX8ojPpfTnRfdAAqzSi3HB/aYRvqRpkESVAVZxxhGduCdyM/VWcEtKbXd73ptgu11AuFsufSfDQanvCmb++Fh2Q46SPnWPPk1G4iQrVrwyAYHCg1vhSpbWRzwjwfj3z7qXbo3pVCbyIw5e39zf9PpIw/eiqbE4fgJHBD8dp3ETk4OPQkQNJE+g8uxMyQy5+uF8sAPvMlYsHXVrzfRNSa0ai7RuCJ5HgTs04ywU204CA7ey9Fua5QBnSg6Bfz+Yh0CTXi0BHpkAvyUChh07CAkShhSQwIR69e7tU+bH+tcSDgQSoYHYW18KrDmDCk4vafSRwTes9rl6+1dNyrcISYH8no+DdFLtGnLnh7Q4oR819nfxIkJa3CV3NfrnKocclh8K+rfB0L3WRF2nh0C7wgFZqRGB5fXWFYrEnxP+HBPisXk+Yse/jp0zw+WQUJ98IR1/+TkkwO0h22xgIYS9nG9HArh/vzK3jjgtvPQRL4CJcsY0qOBicEpAdV6YMTAFCd04XyQgs1suw0J7D5JeRjipdHrgamTAjXvDo4xBG6Gc1gTUGgtRqOcRzC6yCbQbxusL4jTEOqDFYtY1PqJ4u4UDdo8P11WYgHCC/EP9dUxZr8+i46r7Y9iYRnazP744CUm/lSVIzqd7POcKuXqWUNucmpIRuv9ib8LvJrTTwbI7h4ODN4WOtHXSWK37YAbNZRBpw+k5BYV9333mDDpJei7ugi5R7VMlxyod6uLMMRf/QZbMc3IfXfpxYOQqncsZ9wrnyRQ5BL/FOn8XevzCGAE="}],"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1759,"cache_read_input_tokens":49654,"output_tokens":1075,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":1759,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":1075,"cache_read_input_tokens":49654,"cache_creation_input_tokens":1759,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":1759},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRmZazyGURiaCWULgxe","type":"assistant","uuid":"cca6d729-cb50-45e0-965c-4af886b6e525","timestamp":"2026-06-06T17:49:23.855Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"cca6d729-cb50-45e0-965c-4af886b6e525","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01Hc6i3QpQ5WvfRYYS7qbVDF","type":"message","role":"assistant","content":[{"type":"text","text":"Found the issue. `host1.conf` is missing the NPM credentials section entirely — `HOST1_NPM_URL`, `HOST1_NPM_USER`, and `HOST1_NPM_PASS` are never defined. `auth.php` reads `{$host}_NPM_URL/USER/PASS`, so HOST1 falls back to empty strings and auth fails.\n\nHOST2 has the section (lines 643–661 of `host2.conf`) but `HOST2_NPM_USER` and `HOST2_NPM_PASS` are also empty there.\n\nI'll add the auth stack section to `host1.conf` — what are your NPM admin email and password for HOST1?"}],"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1759,"cache_read_input_tokens":49654,"output_tokens":1075,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":1759,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":1075,"cache_read_input_tokens":49654,"cache_creation_input_tokens":1759,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":1759},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRmZazyGURiaCWULgxe","type":"assistant","uuid":"a3993687-9541-4106-a616-a3eb8c295182","timestamp":"2026-06-06T17:49:25.796Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"a3993687-9541-4106-a616-a3eb8c295182","isSidechain":false,"type":"system","subtype":"turn_duration","durationMs":40986,"messageCount":17,"timestamp":"2026-06-06T17:49:25.958Z","uuid":"ca885475-0dc6-42f7-b762-244e957a06be","isMeta":false,"userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"file-history-snapshot","messageId":"d4f6785a-d863-4fcf-b48b-a10c88b79bc8","snapshot":{"messageId":"d4f6785a-d863-4fcf-b48b-a10c88b79bc8","trackedFileBackups":{},"timestamp":"2026-06-06T17:51:52.360Z"},"isSnapshotUpdate":false} {"parentUuid":"ca885475-0dc6-42f7-b762-244e957a06be","isSidechain":false,"promptId":"5c7f5985-7491-48e1-b806-2b47f3112c0a","type":"user","message":{"role":"user","content":"whats causing this... i do a lot of work and a 1/4 of it dissapears"},"uuid":"d4f6785a-d863-4fcf-b48b-a10c88b79bc8","timestamp":"2026-06-06T17:51:52.360Z","permissionMode":"default","promptSource":"typed","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"d4f6785a-d863-4fcf-b48b-a10c88b79bc8","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01K7Tt1L3Asj2KtdMC3Xs6q9","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"EoAYCmUIDhgCKkDFnX7l8iX0oBKcue16fiav3ks5oH9GwM1G56OEDCuvPuwwSKlhHiYvPtTBY8wYTPyQEUD6tbsAp2GOdakyAZQqMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIMqgwmjzWMWJZINwuGGgzYYVvJv7Bie9wgFnUiMJZadGYEenult3Aq1Vuy4Lx+f7aqVFUG5n5xWChVjJWxUoUoL2FvejZBkFpGnNY++irIFjV7k0V7Mq+F+6rhad/RStq/coM0V0uYD3OnKzkmN5oFIq61kfyYHfetbZikB3TqmZdBqDzoPyYwjgy8+GyQCONQUfiQV4/LjcWhTIDrmJBk2aTTuOgp6DcVq/8ADJ/OWQfOmTMTrKMx8Aj1KLrGoIrgdmhSl1HvsxAtfHcQNyvr2w8TlbEMbD/hH1SiyVEPXxt2/F0Cg5clIkd0n54fDRQ3UjWs87McD1rFBMmKB+Q/jdxvGat1TQ6jTwmuvdMDwtGa17dVj3rKyDQWA6QNfM5OyZcAsVfoLEhPRXsJ+hS1K2YRbYl/L5KL11L7GxubpQAPcIRzwuBv0FtkAYz0AZP1afS5oBB1sNJduTxluXeBfJypigLHX2yx5aS7XhfZYM/XPKLcC5fhk/+qt9/ztEg/ydejrZ9jZ9xGGQGmmozl2f7eds4Y5HSY58VNkrW+N42Hq4NcoR717iD04jbuoWUqSdXJHnHG98ij225QzoRLcuyFyZjw+htnyHpfAfbO8WSJX9Fb7kYSusUnWvCIEqS1SJrNTFNZmkyXoAj63T+/kqJyEqGLOOAgq/IqExCiKfP0rVzZ13Sbq6eSYsryI6CIsRIsRwDg6zm8YjJkPoBx50Gt7VKIdlkWN9SF5bpkpzU7zcefvjWnWPwsbvh9nCQD6ovQuLG8pxBVNRVgHRvbOAP7JsU23oUuLNbmMMhPnHnxafPV7KsGLwMNWWGFBkBe1Bd/8pcgHMbXOU7XmhzeO/9ZVVsrOoikj4ZV3tYb51j829YaJUlj31XTLo6TIyMZ8vEHQsVDA4GF7faKXEJivah0k3AgtCAe+PGmPLJpR2GCW0lsbeyWPFO4kdubsDO/A9/suiyDm5VxIMqG7Lz8876dTtIUW71JXccQcm6/UWBwCluWIM6/EJLkj9V4rVyK6bbg+VmPIKGKa009F1ma8G9ZMvdXXlIIP1AByXr5DRinjCwyC9+Htue0Uk5MECnCfiGzen+mic5FRAzakC8Ogp31SC/3PH3PiP13yZskyyE2mYIvu+agQaKEuYyavgud0oZVOQBZNgwtAlVHdXGNH8mR6YhDMOcFyNpEXwAaRIzRTjRqpnt3Nobkh8dozup1YWkp6gBsHLVbjhSmkuSZTRNS/BAQG0eLPqZo+x0DE5VzNgCz5rWRv+MxVShpybF5gOlAGj0GSnQ26uuP+60RsCo8ddKYZD2FUzsVOhPvDqD1EIQ9f+MxglTKMFIks8Zc0tion7YTSqpPE4PjJBRNlRnyNVtTbcpMZ4Zd7HuFSc8qsfel8fFeG/rBiTHQrARttnPle6QziXBu+cgYqmRRSPN8sor1czXShSkQBli4u0PbXPzNW1dfgCQp/l0+2qQqTrimZMXmVvtAhsn1mOQ/hLwKMl1siWg8iZjB3/6F/X608cBmIG8aThDnXHmHkPJP0l3Ay7NeHYpJU70+ObS1bhAT+/D3XBIgKzOD/QJcSLBERBO8GNsdZSRZk5UaBFQKZOkH9CM02xJS5VvsbMHytv9bhUHySgy4qLo9Pn5kELZp46TfUNXlhuLbgJ/u2jAm4yR/TWyKgd4LeUklUVAa+eytBMNhSPEZSYwegGc3fRyXp1bEeyy1cu5MZqhrcW632wIljXOIVYrBqh0hH/kbJTfs/060lGhRJr9k6L4dTN8GH0YluK91h/YaiyQr/8+jxQjZuW5LbHNZcZfMS+P1ctFlo9DrrS/alNTwgdGs1CYNwSJ694BL7HgX5e+JxuSDiY5cCwu0Eqp4uvpU4dsgE0GgBX6LjuHqAfpQFc1xHFSjIBSTUS7HDz2hb4qGgCTooziPfnZg7QMIXkx6hTvx4dkW9lUzEkGSTOQnFLtQbcVpYCrwQQNjnw9+lvRR32MAS1UzXvnBn9lJckuyCB68Mbrjp8pmidiZ1XYDJ23qlTCjU1PTVKr/RFY9PUqZBSs5+hU3Fxal8RPfuFW3cDhu58PT6gfG4tYsry3v2gnpVdlS4okQ4LlVibdl5soorBzy/L9AWc1uvl8QyQe7i8r9taWn4YLIU4k1G1M9uBFO14BTuXBupzUbQS8jUWQ0tA5nCRWb0If+K+WDTMGgdwkhnfhxrvBTpGofQl3le0J4V7bgKgTH9bPSXCjPc4yHClj705DI41Mf593zUeDzYezCO6XZ/TtjYPyXCsMPjQkqeRlqNiDqwEHm6741kbJaHf2IZSXDqGFacgBP768oVo0sD6MxgfmGz5xq1uYcSJEiK3L2ORVL3Rrf3aLaut1Je5dQrugudqWV0Ukrigb8aW1jo3GEezMqVUdkQgj6BjGDeGaXnUNXg1ihI9dv56NT0m4D+lslFJ6Qv6+XQRQkjn54c4yzmc7143cE3kHYGrbBju6LqfXA79VXmg2eRWYvseYzNT1oucmg6JWuZxJKCMGYHMMDYfEx1FPzZdRHV+TnogVvzeykiL5w2jtdZXTeK4BPp+HZhqet4/JdQ9QUDykr6W19biNnmomM/vVsNpIjvjZZiJny2qXWZBZpotuUigrpQOSAEzmgEwDTrHn7Fho78fEmG/YOyoaQf9CDki5F+DDeOiBdRoraL0hNhNxhwcB65JlT+/bxFzHD/INBN9nWhML0mOABf1TLOOzzZJkv1dkxMGLqec2k8IeyXdE6n9iv6qcyaR+kpk4UuXuWTfMIJg83RwXN8tZfLMAeULXR4NwG9e8/LEKtYDv2bfJX0YRf+emT/jYXlX26PKwlnPCjV89ZImi+P+9OTiBFZwMTAM9cAy5rN/w7gaEKsQkTBHNfICErPEn1gTrKBLSBHzPatvFXeFt5ZxiTmuxoNPuIv46tZB7CqT66yU8N9xucdj16BXcuTtlvlKUE+WXIRvMb4i84BQz188MKNaCcuxJUhQNYHoBshbcpM9N8KwmXWzhHtd7dgX8gs0tyas5WRWmYT4E30vovUe+gIVmiRSjqGThXcW2pq0+2ak6Hv8lM5ClRCAbVupPRf2PBi4QmOr/ktXC1RdIZg/sRj1wrstd0vWI0/OhlVIj21X800isBmpoJusllZ2khYz+/tIk9TbGCRqZQDUPpOryW+vWwOgjnoOV44Clg8gUiR8PSPPvhKHnF4OS0Niak1jB398W5Yg1G7hnF0GCVro5/uLmeVXx5xbfegk13BKz/XNrX1qdJCZxBr9tetmT30RFKyhUHccUygZ0K+6Bky7nYup8Gwhsycdt9j6ScEd6Xj9UH/g0YApc9IAiRbn60nsQWtqpwQWrxPZQl+nBGTCqM1dLmb/BWeoYXFyk9rbHDwpRMnwQXnE3DMslUusx10N5B1mTlvaL6bVPDGNFAx6r6f5ScWUttzFmfVGC7dIIq1sq/k1WS0zwVTnqv+MFxD4fmVcpAOIdmJt2//S/Ar70Q9P7MitWDTVXwvk+tzr99JnMPa8AJCCJqBWnpsot56GM28uOIz60MEC1YKsc618pCbW7Sp5wn143mQ+g2tu3eF5jcigY4V8snTcw/0AjY7m2Ln2QMYIDX10e+kPxEzmLf28vEB1NUm4BooNdFgyq0R5JI5eNoU9+G294dMhJt+ZY+AVMJhPhO0FvplkqNpKSGYz2Wp5QcBTqjJW8wnXx83p8PBUMU/s//i1vEko0P8NXLOOuiRPRgPGGH6G6RFFWEHnHU/1oUa0uHHHhgz0WGEouogwa3EeJnVp3lZHemLpjhG8Z/U+BTUqNaMFqgFc5ixybXwVZXZ0sC7Xbd6emJ1PmwdXMwgY7s+6zUuv5Kt/ZLo4iD1Rul+BqPVwdp7nOx/ffSPOsKqF5Xx2qX2kt/leJpIkuRC/8Cev9XRgcFUXWtn+PNSEZPEeQtrz8ST3IUbJ8a2iG8SzTigbMyJiVYGAE="}],"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":26,"cache_read_input_tokens":52486,"output_tokens":916,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":26,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":916,"cache_read_input_tokens":52486,"cache_creation_input_tokens":26,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":26},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRyjg3xeKYbQcDFDU6V","type":"assistant","uuid":"c93b396d-dad6-4a8c-b48e-c46c5e48241d","timestamp":"2026-06-06T17:52:05.331Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"c93b396d-dad6-4a8c-b48e-c46c5e48241d","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01K7Tt1L3Asj2KtdMC3Xs6q9","type":"message","role":"assistant","content":[{"type":"text","text":"Context compression. When a conversation grows long, Claude Code automatically summarizes older messages to fit within the model's context window. The summary captures the broad strokes but loses detail — specific values, nuance, decisions made mid-session.\n\nThe practical effect: the further back something was discussed, the more likely it got compressed into a vague summary or dropped entirely.\n\n**What you can do:**\n\n1. **CLAUDE.md** — put standing facts (credentials, architecture decisions, conventions) in `/boot/config/plugins/varaverk/.claude/CLAUDE.md`. It's always loaded fresh, never compressed.\n\n2. **Memory system** — I have a persistent memory system at `/root/.claude/projects/-root/memory/`. I should be saving key decisions and context there so future sessions start warm. I haven't been doing this aggressively enough for your project specifics.\n\n3. **Shorter sessions** — break big work into focused sessions. One topic per conversation compresses more gracefully than 10 topics in one.\n\n4. **Repeat critical context** — if you're continuing from a prior session on something specific, paste the relevant snippet or file path at the start. I'll re-read it fresh.\n\nWant me to write up a CLAUDE.md for the varaverk project now, capturing the architecture, conventions, and recurring context so it loads every session?"}],"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":26,"cache_read_input_tokens":52486,"output_tokens":916,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":26,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":916,"cache_read_input_tokens":52486,"cache_creation_input_tokens":26,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":26},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnRyjg3xeKYbQcDFDU6V","type":"assistant","uuid":"257da852-7d87-4aa3-b4fe-5669ad4934d0","timestamp":"2026-06-06T17:52:12.353Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"257da852-7d87-4aa3-b4fe-5669ad4934d0","isSidechain":false,"type":"system","subtype":"turn_duration","durationMs":20054,"messageCount":21,"timestamp":"2026-06-06T17:52:12.427Z","uuid":"3a58f13b-679c-426b-96b6-23c4a8fc6a08","isMeta":false,"userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"file-history-snapshot","messageId":"12dacd63-8642-4c62-bbf1-88a1adb3205e","snapshot":{"messageId":"12dacd63-8642-4c62-bbf1-88a1adb3205e","trackedFileBackups":{},"timestamp":"2026-06-06T17:53:16.280Z"},"isSnapshotUpdate":false} {"parentUuid":"3a58f13b-679c-426b-96b6-23c4a8fc6a08","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":"yes, do that. and lets make sure our claud script loads it on an unraid reboot"},"uuid":"12dacd63-8642-4c62-bbf1-88a1adb3205e","timestamp":"2026-06-06T17:53:16.280Z","permissionMode":"default","promptSource":"typed","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"12dacd63-8642-4c62-bbf1-88a1adb3205e","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01JJR82xctAM2u7zZT9VRSpx","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"EuwDCmUIDhgCKkDddZdIWll126WQAKWM/2AGaINDtHOtiQo5YgHjzxpEacmkyd9ROv6HUGXgQxfqm/KJ9VWyV8+oDmKaqEpPJwMtMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIMv9UE1GaO2ZmuCwoUGgzMr91u2h6o45K5CDUiMJ5zMwqvEKsbkoC/UtnqQA/0vGMfMMy4qCIV9Z43MPvXis/72jf9X99YHAzJHGNaOyq0AomkdOw79UZfsYR1yUSaiQko6QAq/vmQ5vmEO/BOYDNQViykzv0Ue1WAwcsgThGCDdMjbn6pfQKhfXhGHEfr1TkNyHKzpuzWLiptoLCLxXHp7PBGhgoFGOclD+X5bORWh1dIzHWpEYi0ahc23Y0v9ANAq1eH3AIqlCFOkzFySxM2qXVDyDG7tmPeWq2GnMirmskM8wGU+X5kSj2l1mptjr86XSbu3x6S99lfB97GG9tEvU3XupVVH9C8de0gDzo2SqiGplBgp9yhrR0GJHbR4KrfoBToX9LaX0LWn9WWy8RGaqJ+uWXZOT2uiveoPZvNLN+9BmzGTFvcmeyTzSY3wyy9YJ1c7uB+E+2cXh/D9IU6f2zoVz0GZxxFxNyFWSZlAALdjpUUXwiQOu4y4R8whFAttGJaGAE="}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":24,"cache_read_input_tokens":53428,"output_tokens":271,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":24,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":271,"cache_read_input_tokens":53428,"cache_creation_input_tokens":24,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":24},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS5vUMUkXhiqvogRLgk","type":"assistant","uuid":"2c76a637-9ae0-45f8-a889-5a0e5a75ff9d","timestamp":"2026-06-06T17:53:19.439Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"2c76a637-9ae0-45f8-a889-5a0e5a75ff9d","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01JJR82xctAM2u7zZT9VRSpx","type":"message","role":"assistant","content":[{"type":"text","text":"Let me read the relevant files first."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":24,"cache_read_input_tokens":53428,"output_tokens":271,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":24,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":271,"cache_read_input_tokens":53428,"cache_creation_input_tokens":24,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":24},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS5vUMUkXhiqvogRLgk","type":"assistant","uuid":"dd09cfea-d8f2-4abc-bdee-fc91158eda2d","timestamp":"2026-06-06T17:53:19.449Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"dd09cfea-d8f2-4abc-bdee-fc91158eda2d","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01JJR82xctAM2u7zZT9VRSpx","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01E49Fm4RpEaZ3CWoVtQm51v","name":"Read","input":{"file_path":"/boot/config/plugins/varaverk/Tools/claude_startup.sh"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":24,"cache_read_input_tokens":53428,"output_tokens":271,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":24,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":271,"cache_read_input_tokens":53428,"cache_creation_input_tokens":24,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":24},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS5vUMUkXhiqvogRLgk","type":"assistant","uuid":"ef8e23fd-a1e6-41e8-9af0-90d8e231390f","timestamp":"2026-06-06T17:53:20.296Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"ef8e23fd-a1e6-41e8-9af0-90d8e231390f","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01E49Fm4RpEaZ3CWoVtQm51v","type":"tool_result","content":"1\t#!/bin/bash\n2\t# ==============================================================================================\n3\t# ================================= Claude Code Startup ========================================\n4\t# ==============================================================================================\n5\t#\n6\t# PURPOSE\n7\t# ─────────────────────────────────────────────────────────────────────────────\n8\t# Restores Claude Code's persistent data after an Unraid reboot and launches Claude.\n9\t# Unraid's root filesystem lives in RAM — /root/.claude and /root/.local are wiped on\n10\t# every reboot. This script symlinks both directories back to persistent appdata storage\n11\t# before launching Claude, so memory, sessions, and settings survive across reboots.\n12\t#\n13\t# On first run with no existing persistent data, migrates from the current live locations:\n14\t# /root/.claude → PERSIST_DIR/.claude (memory, sessions, settings)\n15\t# /root/.local/share/claude → PERSIST_DIR/local/share/claude (installed binaries)\n16\t# Subsequent runs skip the migration and only create the symlinks.\n17\t#\n18\t# Standalone script — no common.sh dependency. Safe to run directly from terminal\n19\t# or from array_started.sh.\n20\t#\n21\t# ==============================================================================================\n22\t# RUNTIME MODES\n23\t# ==============================================================================================\n24\t#\n25\t# claude_startup.sh\n26\t# Set up persistent symlinks only — default, used by array_started.sh on boot.\n27\t#\n28\t# claude_startup.sh --launch\n29\t# Set up persistent symlinks and launch Claude interactively.\n30\t#\n31\t# ==============================================================================================\n32\t\n33\tPERSIST_DIR=\"/mnt/user/appdata/claude-code\"\n34\tCLAUDE_DATA=\"$PERSIST_DIR/.claude\"\n35\tCLAUDE_BIN=\"$PERSIST_DIR/local/share/claude\"\n36\t\n37\tLAUNCH=false\n38\t[[ \"$1\" == \"--launch\" ]] && LAUNCH=true\n39\t\n40\t# Standalone — no common.sh dependency\n41\t_log() { echo \" ✅ $*\"; }\n42\t_warn() { echo \" ⚠️ $*\"; }\n43\t_err() { echo \" ❌ $*\" >&2; }\n44\t\n45\techo \"\"\n46\techo \"━━━ Claude Code Startup ━━━\"\n47\techo \"\"\n48\t\n49\t# ── Array must be mounted ─────────────────────────────────────────────────────────────────────\n50\tif ! mountpoint -q /mnt/user 2>/dev/null; then\n51\t _err \"Array not mounted — /mnt/user not available\"\n52\t exit 1\n53\tfi\n54\t\n55\t# ── Create persistent dirs ────────────────────────────────────────────────────────────────────\n56\tmkdir -p \"$CLAUDE_DATA\" \"$CLAUDE_BIN\"\n57\t\n58\t# ── Migrate .claude on first run ──────────────────────────────────────────────────────────────\n59\tif [[ ! -L /root/.claude && -d /root/.claude ]]; then\n60\t _warn \"First run — migrating /root/.claude → $CLAUDE_DATA\"\n61\t cp -a /root/.claude/. \"$CLAUDE_DATA/\"\n62\t rm -rf /root/.claude\n63\t _log \"Migrated .claude (memory, sessions, settings)\"\n64\telif [[ -z \"$(ls -A \"$CLAUDE_DATA\" 2>/dev/null)\" && -d /root/.claude ]]; then\n65\t _warn \"Persistent storage empty — copying current .claude data\"\n66\t cp -a /root/.claude/. \"$CLAUDE_DATA/\"\n67\t _log \"Copied .claude data to persistent storage\"\n68\tfi\n69\t\n70\t# ── Migrate Claude binaries on first run ──────────────────────────────────────────────────────\n71\tif [[ ! -L /root/.local/share/claude && -d /root/.local/share/claude ]]; then\n72\t _warn \"First run — migrating Claude binaries → $CLAUDE_BIN\"\n73\t cp -a /root/.local/share/claude/. \"$CLAUDE_BIN/\"\n74\t _log \"Migrated Claude binaries\"\n75\tfi\n76\t\n77\t# ── Create symlinks ───────────────────────────────────────────────────────────────────────────\n78\t# Remove any real directories first — ln -sfn silently creates inside a dir instead of\n79\t# replacing it, which produces a circular symlink on subsequent runs after migration.\n80\tmkdir -p /root/.local/share /root/.local/bin\n81\t\n82\t[[ -d /root/.claude && ! -L /root/.claude ]] && rm -rf /root/.claude\n83\tln -sfn \"$CLAUDE_DATA\" /root/.claude\n84\t_log \".claude → $CLAUDE_DATA\"\n85\t\n86\t[[ -d /root/.local/share/claude && ! -L /root/.local/share/claude ]] && rm -rf /root/.local/share/claude\n87\tln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude\n88\t_log \"claude binary → $CLAUDE_BIN\"\n89\t\n90\t# ── Point the claude binary at the latest installed version ───────────────────────────────────\n91\tLATEST=$(ls \"$CLAUDE_BIN/versions/\" 2>/dev/null | sort -V | tail -1)\n92\tif [[ -z \"$LATEST\" ]]; then\n93\t _err \"No Claude versions found in $CLAUDE_BIN/versions/\"\n94\t _err \"Install Claude Code first: npm install -g @anthropic-ai/claude-code\"\n95\t exit 1\n96\tfi\n97\t\n98\tln -sfn \"$CLAUDE_BIN/versions/$LATEST\" /root/.local/bin/claude\n99\t_log \"claude v$LATEST ready\"\n100\t\n101\techo \"\"\n102\t\n103\t# ── Setup-only mode (used by array_started.sh or other callers) ─────────────────────────────────\n104\tif [[ \"$LAUNCH\" == false ]]; then\n105\t _log \"Setup complete — run 'claude' to start\"\n106\t echo \"\"\n107\t exit 0\n108\tfi\n109\t\n110\t# ── Launch ────────────────────────────────────────────────────────────────────────────────────\n111\texport PATH=\"$HOME/.local/bin:$PATH\"\n112\texec claude\n113\t"}]},"uuid":"1e88674b-3dd3-4443-a7fe-bc94c1274bf6","timestamp":"2026-06-06T17:53:20.316Z","toolUseResult":{"type":"text","file":{"filePath":"/boot/config/plugins/varaverk/Tools/claude_startup.sh","content":"#!/bin/bash\n# ==============================================================================================\n# ================================= Claude Code Startup ========================================\n# ==============================================================================================\n#\n# PURPOSE\n# ─────────────────────────────────────────────────────────────────────────────\n# Restores Claude Code's persistent data after an Unraid reboot and launches Claude.\n# Unraid's root filesystem lives in RAM — /root/.claude and /root/.local are wiped on\n# every reboot. This script symlinks both directories back to persistent appdata storage\n# before launching Claude, so memory, sessions, and settings survive across reboots.\n#\n# On first run with no existing persistent data, migrates from the current live locations:\n# /root/.claude → PERSIST_DIR/.claude (memory, sessions, settings)\n# /root/.local/share/claude → PERSIST_DIR/local/share/claude (installed binaries)\n# Subsequent runs skip the migration and only create the symlinks.\n#\n# Standalone script — no common.sh dependency. Safe to run directly from terminal\n# or from array_started.sh.\n#\n# ==============================================================================================\n# RUNTIME MODES\n# ==============================================================================================\n#\n# claude_startup.sh\n# Set up persistent symlinks only — default, used by array_started.sh on boot.\n#\n# claude_startup.sh --launch\n# Set up persistent symlinks and launch Claude interactively.\n#\n# ==============================================================================================\n\nPERSIST_DIR=\"/mnt/user/appdata/claude-code\"\nCLAUDE_DATA=\"$PERSIST_DIR/.claude\"\nCLAUDE_BIN=\"$PERSIST_DIR/local/share/claude\"\n\nLAUNCH=false\n[[ \"$1\" == \"--launch\" ]] && LAUNCH=true\n\n# Standalone — no common.sh dependency\n_log() { echo \" ✅ $*\"; }\n_warn() { echo \" ⚠️ $*\"; }\n_err() { echo \" ❌ $*\" >&2; }\n\necho \"\"\necho \"━━━ Claude Code Startup ━━━\"\necho \"\"\n\n# ── Array must be mounted ─────────────────────────────────────────────────────────────────────\nif ! mountpoint -q /mnt/user 2>/dev/null; then\n _err \"Array not mounted — /mnt/user not available\"\n exit 1\nfi\n\n# ── Create persistent dirs ────────────────────────────────────────────────────────────────────\nmkdir -p \"$CLAUDE_DATA\" \"$CLAUDE_BIN\"\n\n# ── Migrate .claude on first run ──────────────────────────────────────────────────────────────\nif [[ ! -L /root/.claude && -d /root/.claude ]]; then\n _warn \"First run — migrating /root/.claude → $CLAUDE_DATA\"\n cp -a /root/.claude/. \"$CLAUDE_DATA/\"\n rm -rf /root/.claude\n _log \"Migrated .claude (memory, sessions, settings)\"\nelif [[ -z \"$(ls -A \"$CLAUDE_DATA\" 2>/dev/null)\" && -d /root/.claude ]]; then\n _warn \"Persistent storage empty — copying current .claude data\"\n cp -a /root/.claude/. \"$CLAUDE_DATA/\"\n _log \"Copied .claude data to persistent storage\"\nfi\n\n# ── Migrate Claude binaries on first run ──────────────────────────────────────────────────────\nif [[ ! -L /root/.local/share/claude && -d /root/.local/share/claude ]]; then\n _warn \"First run — migrating Claude binaries → $CLAUDE_BIN\"\n cp -a /root/.local/share/claude/. \"$CLAUDE_BIN/\"\n _log \"Migrated Claude binaries\"\nfi\n\n# ── Create symlinks ───────────────────────────────────────────────────────────────────────────\n# Remove any real directories first — ln -sfn silently creates inside a dir instead of\n# replacing it, which produces a circular symlink on subsequent runs after migration.\nmkdir -p /root/.local/share /root/.local/bin\n\n[[ -d /root/.claude && ! -L /root/.claude ]] && rm -rf /root/.claude\nln -sfn \"$CLAUDE_DATA\" /root/.claude\n_log \".claude → $CLAUDE_DATA\"\n\n[[ -d /root/.local/share/claude && ! -L /root/.local/share/claude ]] && rm -rf /root/.local/share/claude\nln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude\n_log \"claude binary → $CLAUDE_BIN\"\n\n# ── Point the claude binary at the latest installed version ───────────────────────────────────\nLATEST=$(ls \"$CLAUDE_BIN/versions/\" 2>/dev/null | sort -V | tail -1)\nif [[ -z \"$LATEST\" ]]; then\n _err \"No Claude versions found in $CLAUDE_BIN/versions/\"\n _err \"Install Claude Code first: npm install -g @anthropic-ai/claude-code\"\n exit 1\nfi\n\nln -sfn \"$CLAUDE_BIN/versions/$LATEST\" /root/.local/bin/claude\n_log \"claude v$LATEST ready\"\n\necho \"\"\n\n# ── Setup-only mode (used by array_started.sh or other callers) ─────────────────────────────────\nif [[ \"$LAUNCH\" == false ]]; then\n _log \"Setup complete — run 'claude' to start\"\n echo \"\"\n exit 0\nfi\n\n# ── Launch ────────────────────────────────────────────────────────────────────────────────────\nexport PATH=\"$HOME/.local/bin:$PATH\"\nexec claude\n","numLines":113,"startLine":1,"totalLines":113}},"sourceToolAssistantUUID":"ef8e23fd-a1e6-41e8-9af0-90d8e231390f","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"1e88674b-3dd3-4443-a7fe-bc94c1274bf6","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01JJR82xctAM2u7zZT9VRSpx","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_018bRNCmprBRSFdK1JwQnRAp","name":"Bash","input":{"command":"cat /boot/config/plugins/varaverk/.claude/settings.json 2>/dev/null; ls /boot/config/plugins/varaverk/.claude/ 2>/dev/null","description":"Check existing claude settings and directory"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":24,"cache_read_input_tokens":53428,"output_tokens":271,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":24,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":271,"cache_read_input_tokens":53428,"cache_creation_input_tokens":24,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":24},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS5vUMUkXhiqvogRLgk","type":"assistant","uuid":"ac391537-f495-4f43-abfd-b5a31e299efb","timestamp":"2026-06-06T17:53:21.105Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"ac391537-f495-4f43-abfd-b5a31e299efb","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_018bRNCmprBRSFdK1JwQnRAp","type":"tool_result","content":"{\n \"hooks\": {\n \"PostToolUse\": [\n {\n \"matcher\": \"Edit|Write\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"bash '/mnt/cloud-storage/Important Shit/Git/Development/Varaverk/Deployment/deploy.sh'\"\n }\n ]\n }\n ]\n }\n}\nsettings.json","is_error":false}]},"uuid":"3ded8494-f651-469b-8aa8-78a47c667278","timestamp":"2026-06-06T17:53:21.639Z","toolUseResult":{"stdout":"{\n \"hooks\": {\n \"PostToolUse\": [\n {\n \"matcher\": \"Edit|Write\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"bash '/mnt/cloud-storage/Important Shit/Git/Development/Varaverk/Deployment/deploy.sh'\"\n }\n ]\n }\n ]\n }\n}\nsettings.json","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"ac391537-f495-4f43-abfd-b5a31e299efb","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"3ded8494-f651-469b-8aa8-78a47c667278","isSidechain":false,"attachment":{"type":"task_reminder","content":[],"itemCount":0},"type":"attachment","uuid":"a54bd6e6-8f7b-49a7-b0f4-1717cdcbb73a","timestamp":"2026-06-06T17:53:21.641Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"last-prompt","lastPrompt":"yes, do that. and lets make sure our claud script loads it on an unraid reboot","leafUuid":"a54bd6e6-8f7b-49a7-b0f4-1717cdcbb73a","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"ai-title","aiTitle":"Troubleshoot NPM authentication failure","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"mode","mode":"normal","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"permission-mode","permissionMode":"default","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"parentUuid":"a54bd6e6-8f7b-49a7-b0f4-1717cdcbb73a","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01TtiG7wHrKgwBmCSMVXVYpM","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_018yoDgGk4jc59ouMoKAcQHz","name":"Read","input":{"file_path":"/root/.claude/projects/-root/memory/project_varaverk.md"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2178,"cache_read_input_tokens":53452,"output_tokens":173,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":2178,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":173,"cache_read_input_tokens":53452,"cache_creation_input_tokens":2178,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":2178},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6KPzz3RNrznxHhkDH","type":"assistant","uuid":"48febda9-c1a3-40b3-bb55-2af8d1863079","timestamp":"2026-06-06T17:53:24.431Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"48febda9-c1a3-40b3-bb55-2af8d1863079","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_018yoDgGk4jc59ouMoKAcQHz","type":"tool_result","content":"1\t---\n2\tname: project_varaverk\n3\tdescription: Varaverk — self-healing mutually-redundant two-server Unraid home media ecosystem\n4\tmetadata: \n5\t node_type: memory\n6\t type: project\n7\t originSessionId: a1ac522e-9103-4757-b6d0-a7cd2f5681fb\n8\t---\n9\t\n10\t**Varaverk** is a complete self-healing, self-maintaining, mutually-redundant two-server home server ecosystem. One codebase runs on both servers. No primary/standby — both servers run independently and cover each other when one goes down.\n11\t\n12\t## The Two Servers\n13\t\n14\t**HOST1 — unRAID-Gmer4Lfe**\n15\t- Hardware: Threadripper 1950X, 128GB RAM, ZFS cache pools\n16\t- Location: Primary site\n17\t- Domain: Gmer4Lfe.com\n18\t- Runs: Arrs (Movies, TV, Music), Auth stack (source of truth), Emby (primary)\n19\t\n20\t**HOST2 — unRAID-Jayred365**\n21\t- Hardware: Intel i5 10th gen, 64GB RAM\n22\t- Location: Remote — different building, different power utility\n23\t- Domain: Gmer4Lfe.us\n24\t\n25\t## Architecture\n26\t- Platform adapter layer (Plugin/unraid/adapter.sh) isolates OS-specific calls — scripts never branch on OS\n27\t- Self-healing, not enterprise HA — goal is minimal media stack disruption\n28\t- Tailscale for mesh networking between hosts\n29\t\n30\t## Active To-Dos (from Notes_To-Do.md)\n31\t- Fix fallback strike list timing: ~30s first, ~90s for 3-strike trigger — needs testing\n32\t- Verify silent toggle switches back on good notifications\n33\t- Rename folder Unraid_Scripts → Varaverk everywhere, update git script, all traces/scripts\n34\t\n35\t## Future Design Ideas\n36\t- Shared auth stack for partner hosts to start shared services\n37\t- When owner offboards with 2+ servers: auto-promote strongest server (by compute + bandwidth)\n38\t- Overall setup script that pulls vars automatically (docker names, etc.)\n39\t- App layer as king: no more direct git — app opens/edits settings, partnership deploys to servers, pushes correct host.conf\n40\t- Web UI: on initial launch with no state file, open master.conf; lock orchs until setup complete\n41\t- First-launch guide: owner sets up master.conf → host1.conf → Tailscale shares → onboard → host2/3 install and see state file, default to mirror mode\n42\t\n43\t**Why:** User is building this as a personal project on Unraid. Design philosophy favors simplicity and independence over enterprise tooling.\n44\t**How to apply:** Understand the two-server mesh model when suggesting architecture. The app layer / web UI direction is the current strategic focus — moving away from raw git/scripts toward a proper application.\n45\t"}]},"uuid":"20883cde-4559-428c-95d0-5a508f62e04e","timestamp":"2026-06-06T17:53:24.454Z","toolUseResult":{"type":"text","file":{"filePath":"/root/.claude/projects/-root/memory/project_varaverk.md","content":"---\nname: project_varaverk\ndescription: Varaverk — self-healing mutually-redundant two-server Unraid home media ecosystem\nmetadata: \n node_type: memory\n type: project\n originSessionId: a1ac522e-9103-4757-b6d0-a7cd2f5681fb\n---\n\n**Varaverk** is a complete self-healing, self-maintaining, mutually-redundant two-server home server ecosystem. One codebase runs on both servers. No primary/standby — both servers run independently and cover each other when one goes down.\n\n## The Two Servers\n\n**HOST1 — unRAID-Gmer4Lfe**\n- Hardware: Threadripper 1950X, 128GB RAM, ZFS cache pools\n- Location: Primary site\n- Domain: Gmer4Lfe.com\n- Runs: Arrs (Movies, TV, Music), Auth stack (source of truth), Emby (primary)\n\n**HOST2 — unRAID-Jayred365**\n- Hardware: Intel i5 10th gen, 64GB RAM\n- Location: Remote — different building, different power utility\n- Domain: Gmer4Lfe.us\n\n## Architecture\n- Platform adapter layer (Plugin/unraid/adapter.sh) isolates OS-specific calls — scripts never branch on OS\n- Self-healing, not enterprise HA — goal is minimal media stack disruption\n- Tailscale for mesh networking between hosts\n\n## Active To-Dos (from Notes_To-Do.md)\n- Fix fallback strike list timing: ~30s first, ~90s for 3-strike trigger — needs testing\n- Verify silent toggle switches back on good notifications\n- Rename folder Unraid_Scripts → Varaverk everywhere, update git script, all traces/scripts\n\n## Future Design Ideas\n- Shared auth stack for partner hosts to start shared services\n- When owner offboards with 2+ servers: auto-promote strongest server (by compute + bandwidth)\n- Overall setup script that pulls vars automatically (docker names, etc.)\n- App layer as king: no more direct git — app opens/edits settings, partnership deploys to servers, pushes correct host.conf\n- Web UI: on initial launch with no state file, open master.conf; lock orchs until setup complete\n- First-launch guide: owner sets up master.conf → host1.conf → Tailscale shares → onboard → host2/3 install and see state file, default to mirror mode\n\n**Why:** User is building this as a personal project on Unraid. Design philosophy favors simplicity and independence over enterprise tooling.\n**How to apply:** Understand the two-server mesh model when suggesting architecture. The app layer / web UI direction is the current strategic focus — moving away from raw git/scripts toward a proper application.\n","numLines":45,"startLine":1,"totalLines":45}},"sourceToolAssistantUUID":"48febda9-c1a3-40b3-bb55-2af8d1863079","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"20883cde-4559-428c-95d0-5a508f62e04e","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01TtiG7wHrKgwBmCSMVXVYpM","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01E1hDBRbvWMi3ro8RBgExhS","name":"Read","input":{"file_path":"/root/.claude/projects/-root/memory/user_profile.md"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2178,"cache_read_input_tokens":53452,"output_tokens":173,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":2178,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":173,"cache_read_input_tokens":53452,"cache_creation_input_tokens":2178,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":2178},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6KPzz3RNrznxHhkDH","type":"assistant","uuid":"956befab-71d1-4af4-8d45-66be3a7bfbb1","timestamp":"2026-06-06T17:53:24.854Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"956befab-71d1-4af4-8d45-66be3a7bfbb1","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01E1hDBRbvWMi3ro8RBgExhS","type":"tool_result","content":"1\t---\n2\tname: user_profile\n3\tdescription: User profile — Unraid home server operator building Varaverk\n4\tmetadata: \n5\t node_type: memory\n6\t type: user\n7\t originSessionId: a1ac522e-9103-4757-b6d0-a7cd2f5681fb\n8\t---\n9\t\n10\tRuns Unraid on two home servers (HOST1: Threadripper 1950X, HOST2: Intel i5 10th gen).\n11\tBuilding Varaverk — a self-healing two-server media ecosystem.\n12\tEmail: gmer4lfe@gmail.com\n13\tDomain: Gmer4Lfe.com / Gmer4Lfe.us\n14\t\n15\tPractical, hands-on operator. Thinks in design/architecture terms but works at the script/config level.\n16\tDeals with real-world issues: power outages, reboots, reinstalls.\n17\t\n18\t**Note:** Claude Code does not persist memory across Unraid reboots unless memory files are on /boot (USB). After any reinstall, point Claude at /boot/config/plugins/varaverk to restore context.\n19\t"}]},"uuid":"6a25051a-322f-44fc-bd31-5631006d01aa","timestamp":"2026-06-06T17:53:24.871Z","toolUseResult":{"type":"text","file":{"filePath":"/root/.claude/projects/-root/memory/user_profile.md","content":"---\nname: user_profile\ndescription: User profile — Unraid home server operator building Varaverk\nmetadata: \n node_type: memory\n type: user\n originSessionId: a1ac522e-9103-4757-b6d0-a7cd2f5681fb\n---\n\nRuns Unraid on two home servers (HOST1: Threadripper 1950X, HOST2: Intel i5 10th gen).\nBuilding Varaverk — a self-healing two-server media ecosystem.\nEmail: gmer4lfe@gmail.com\nDomain: Gmer4Lfe.com / Gmer4Lfe.us\n\nPractical, hands-on operator. Thinks in design/architecture terms but works at the script/config level.\nDeals with real-world issues: power outages, reboots, reinstalls.\n\n**Note:** Claude Code does not persist memory across Unraid reboots unless memory files are on /boot (USB). After any reinstall, point Claude at /boot/config/plugins/varaverk to restore context.\n","numLines":19,"startLine":1,"totalLines":19}},"sourceToolAssistantUUID":"956befab-71d1-4af4-8d45-66be3a7bfbb1","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"6a25051a-322f-44fc-bd31-5631006d01aa","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01TtiG7wHrKgwBmCSMVXVYpM","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Ez511ZkUhrK3gvwhPLbN4Y","name":"Read","input":{"file_path":"/boot/config/plugins/varaverk/Configurations/master.conf"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2178,"cache_read_input_tokens":53452,"output_tokens":173,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":2178,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":173,"cache_read_input_tokens":53452,"cache_creation_input_tokens":2178,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":2178},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6KPzz3RNrznxHhkDH","type":"assistant","uuid":"aaac3e1e-059c-4eb8-8fa9-93febe5db3b4","timestamp":"2026-06-06T17:53:25.423Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"aaac3e1e-059c-4eb8-8fa9-93febe5db3b4","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Ez511ZkUhrK3gvwhPLbN4Y","type":"tool_result","content":"1\t#!/bin/bash\n2\t# ==============================================================================================\n3\t# ================================= MASTER CONFIGURATION =======================================\n4\t# ==============================================================================================\n5\t# Shared configuration for the unRAID script ecosystem.\n6\t# Contains all settings that apply to every server — thresholds, toggles, profiles, job lists.\n7\t#\n8\t# ── HOW THE THREE-FILE SYSTEM WORKS ──────────────────────────────────────────────────────────\n9\t# Scripts source all three files at startup:\n10\t# source master.conf ← shared config (this file)\n11\t# source host1.conf ← HOST1 credentials, shares, container lists\n12\t# source host2.conf ← HOST2 credentials, shares, container lists\n13\t#\n14\t# Sparse checkout (git) ensures each server only pulls its own host*.conf.\n15\t# HOST2 never sees HOST1 credentials. HOST1 never sees HOST2 credentials.\n16\t#\n17\t# What belongs here: thresholds, toggles, intervals, profiles, job lists\n18\t# What belongs in HOST*: hostnames, SSH keys, API keys, passwords, share paths,\n19\t# container names, failover lists, watchdog containers\n20\t#\n21\t# ── HOW THIS FILE WORKS ───────────────────────────────────────────────────────────────────────\n22\t# Every script sources all three conf files and common.sh at startup.\n23\t# Change a value here and it affects all scripts that use it — no hunting through files.\n24\t# To disable something: comment it out with # rather than deleting it.\n25\t# To add a new rsync profile: add a key to each PROFILE_* array in the RSYNC section.\n26\t# To add or remove orchestrator jobs: edit the arrays in the ORCHESTRATORS section.\n27\t#\n28\t# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────\n29\t#\n30\t# Section Description\n31\t# ───────────────────────────────────────────────────────────────────────────────────────────\n32\t# SHARED HOST CONFIGURATION DATA_DIR and shared connection settings\n33\t# PARTNERSHIP Mirror relationship lifecycle — onboard/offboard/transfer\n34\t# LOGGING Enable or disable verbose logging\n35\t# NOTIFICATIONS unRAID native and Discord webhook settings\n36\t# GIT / REPO Gitea repository and SSH settings\n37\t#\n38\t# ── ORCHESTRATORS ──────────────────────────────────────────────────────────────────────────\n39\t# ARRAY START Scripts launched at array start (array_started.sh)\n40\t# ARRAY STOP Scripts run at planned shutdown (array_stopping.sh)\n41\t# WATCHDOG ORCHESTRATOR Per-minute watchdog runner (watchdog_orchestrator.sh)\n42\t# SYSTEM WATCHDOG Sub-scripts called by watchdog_orchestrator.sh\n43\t# CRITICAL SYNC MAINTENANCE 30-minute jobs + sync shares + partnership check (critical_sync_maintenance.sh)\n44\t# INTERMEDIATE SYNC MAINTENANCE Arr sync + optional mid-day rsync (intermediate_sync_maintenance.sh)\n45\t# DAILY SYNC MAINTENANCE Job list + media shares (daily_sync_maintenance.sh)\n46\t# WEEKLY SYNC MAINTENANCE Job list + sync shares + update toggles (weekly_sync_maintenance.sh)\n47\t# MONTHLY MAINTENANCE Uptime-triggered heavy tasks — ZFS scrub, SMART tests (monthly_maintenance.sh)\n48\t#\n49\t# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────\n50\t# RSYNC ENABLE/DISABLE Two-tier toggle system — global gate + per-orchestrator\n51\t# RSYNC DEFAULTS Global fallback rsync options and limits\n52\t# REMOTE HEALTH CHECKS Rootfs threshold for pre-flight abort\n53\t# RSYNC PROFILE SYSTEM Per-profile overrides for appdata syncs\n54\t#\n55\t# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────\n56\t# FALLBACK Mutual container failover shared settings\n57\t# FALLBACK TEST Simulated outage settings for fallback_test.sh\n58\t#\n59\t# ── DOCKER ESSENTIALS ──────────────────────────────────────────────────────────────────────\n60\t# DOWNLOADERS RESET Retention and thresholds for slskd, SABnzbd, qBittorrent\n61\t# DOCKER DAILY RESTART Containers restarted daily\n62\t# DOCKER WEEKLY RESTART Containers restarted weekly\n63\t# DOCKER WATCHDOG Thresholds and toggles for container monitoring\n64\t# DOCKER NETWORK CONNECT Networks to ensure + containers to connect\n65\t#\n66\t# ── UNRAID ESSENTIALS ──────────────────────────────────────────────────────────────────────\n67\t# INOTIFY TUNING inotify limits — raised at array start by inotify_tuning.sh\n68\t# SYSTEM TUNING MONITOR Tracks inotify + php-fpm usage over time\n69\t# REBOOT User warning delay before scheduled reboot\n70\t# MOVER Mover stop timeout\n71\t# SYSLOG FILTER Docker veth noise filter file path\n72\t# PHP-FPM PHP-FPM max children config\n73\t# CLEAR LOGS System log file paths\n74\t# WEBGUI WATCHDOG WebGUI nginx + emhttp monitoring and restart\n75\t#\n76\t# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────\n77\t# MEDIA PERMISSIONS Share list, mode and owner for permissions script\n78\t# MEDIA CLEANER Anime and media folder lists and file patterns\n79\t# ARR CLEANUP Lidarr, Sonarr, Radarr orphan file cleanup settings\n80\t# ARR FAILED/STALLED RECOVERY Auto blocklist + re-search settings\n81\t#\n82\t# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────\n83\t# TRANSCODE MANAGER Ramdisk and SSD fallback transcode management\n84\t# TRANSCODE SERVER ARRAY Multi-server session monitoring (Emby, Jellyfin, Plex)\n85\t#\n86\t# ── MONITORS ───────────────────────────────────────────────────────────────────────────────\n87\t# CERTIFICATE MONITOR SSL certificate expiry monitoring\n88\t# BACKUP VERIFY Random sample checksum verification against remote\n89\t# SMART HEALTH Drive SMART attribute monitoring\n90\t# ZFS MEMORY SNAPSHOT Weekly ZFS health and memory diagnostic report\n91\t# BANDWIDTH MONITOR Daily rsync transfer logging and weekly summary\n92\t# HEALTH DIGEST Aggregated system health digest — always/smart/weekly\n93\t# EMBY SESSION REPORT Weekly Emby usage statistics via API\n94\t#\n95\t# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────\n96\t# SYSTEM WATCHDOG Continuous system health monitoring — last line of defense\n97\t#\n98\t# ==============================================================================================\n99\t\n100\t# ==============================================================================================\n101\t# ── HOST IDENTITIES ───────────────────────────────────────────────────────────────────────────\n102\t# ==============================================================================================\n103\t# Hostnames for every server in the ecosystem — not credentials, safe for all machines.\n104\t# Must match the exact unRAID hostname AND Tailscale device name (case sensitive).\n105\t# detect_hosts() in common.sh matches the local hostname against these to set MY_ID / REMOTE_ID.\n106\t# Tailscale IP resolution uses these names — no hardcoded IPs needed.\n107\t# To add a new server: add HOST3=\"unRAID-NewServer\" here + create host3.conf.\n108\t HOST1=\"unRAID-Gmer4Lfe\"\n109\t HOST2=\"unRAID-Jayred365\"\n110\t\n111\t# ==============================================================================================\n112\t# ── SHARED HOST CONFIGURATION ─────────────────────────────────────────────────────────────────\n113\t# ==============================================================================================\n114\t# Requirement: internal NVMe/SSD boot drive. Everything lives in the plugin folder on flash —\n115\t# scripts, state, and data are all available before the array mounts.\n116\t# Both directories are created automatically if they don't exist.\n117\t#\n118\t# DATA_DIR — historical logs, statistics, discovery histories, blocklists\n119\t# STATE_DIR — runtime state files for all scripts (watchdogs, fallback, transcode, etc.)\n120\t# Requirement: ALL state files MUST use $STATE_DIR. No /tmp, no /boot/config root.\n121\t DATA_DIR=\"/boot/config/plugins/varaverk/data\"\n122\t STATE_DIR=\"/boot/config/plugins/varaverk/State_Files\"\n123\t\n124\t# ── Version Parity ──\n125\t# Controls behaviour when local and remote unRAID versions differ.\n126\t# Major version mismatch always aborts regardless of this setting.\n127\t# \"warn\" — log warning and continue (safe for minor/patch differences)\n128\t# \"abort\" — refuse to continue (strict — ensures both sides always match)\n129\t UNRAID_VERSION_MISMATCH_ACTION=\"warn\"\n130\t\n131\t# ── Arr Sync ──\n132\t# arr_sync.sh syncs Lidarr/Sonarr/Radarr libraries across all nodes bidirectionally.\n133\t# Runs before rsync — all nodes agree on tracked library before files are transferred.\n134\t# Remote API keys are read live from each node's config.xml via SSH — never stored here.\n135\t ARR_SYNC_ENABLED=true\n136\t ARR_SYNC_BLOCKLIST=\"${DATA_DIR}/arr_sync_blocklist.tsv\"\n137\t ARR_SYNC_CONNECT_TIMEOUT=10 # seconds — SSH connect timeout per node\n138\t ARR_SYNC_API_TIMEOUT=60 # seconds — curl timeout for library fetches\n139\t DOCKER_APPDATA_BASE=\"/mnt/user/appdata\"\n140\t ARR_SYNC_LIDARR_PORT=8686\n141\t ARR_SYNC_SONARR_PORT=8989\n142\t ARR_SYNC_RADARR_PORT=7878\n143\t\n144\t# ── Remote Docker Daemon ──\n145\t# Strike system for remote Docker daemon health checks.\n146\t# Scripts that issue remote container commands check the remote daemon first.\n147\t# Below limit → skip operation this run | At limit → notify critical + exit\n148\t REMOTE_DOCKER_STRIKE_LIMIT=3 # consecutive failures before critical notify\n149\t\n150\t# ==============================================================================================\n151\t# ── PARTNERSHIP ───────────────────────────────────────────────────────────────────────────────\n152\t# ==============================================================================================\n153\t# Manages the relationship lifecycle between two unRAID servers.\n154\t# HOST1 is always the owner (source of truth) — HOST2 is always the mirror.\n155\t# PARTNERSHIP_OWNER_HOST flips to \"HOST2\" after a --transfer operation.\n156\t# All identity vars (hostnames, SSH keys) live in host*.conf.\n157\t# Hostnames already match Tailscale device names — IP resolution is automatic.\n158\t#\n159\t# State files in STATE_DIR — survives reboots, array must be up (scripts run after array start):\n160\t# STATE_DIR/partnership_HOST1.db ← HOST1 writes only\n161\t# STATE_DIR/partnership_HOST2.db ← HOST2 writes only\n162\t# STATE_DIR/partnership_blocklist.db ← shared blocklist\n163\t# STATE_DIR/varaverk_setup.db ← onboarding progress state\n164\t# Propagated via SSH — no rsync needed\n165\t#\n166\t# critical_sync_maintenance.sh runs --check every 30min:\n167\t# Reads both state files via SSH\n168\t# Detects offboard requests → finalises from owner side\n169\t# Increments offline counter → auto-offboards after threshold\n170\t# Silent when healthy ✅\n171\t#\n172\t# See README-Partnership.md for full lifecycle documentation.\n173\t\n174\t PARTNERSHIP_ENABLED=false\n175\t PARTNERSHIP_OWNER_HOST=\"HOST1\" # \"HOST1\" or \"HOST2\" — flips on --transfer\n176\t\n177\t# Auth containers reconfigured on onboard/offboard — defined per host in host*.conf.\n178\t# Format: \"ContainerName|WebUIPort\"\n179\t# HOST1_PARTNERSHIP_AUTH_WEBUIS / HOST2_PARTNERSHIP_AUTH_WEBUIS\n180\t# On onboard → WebUI pointed at owner's Tailscale IP\n181\t# On offboard → WebUI pointed back at localhost\n182\t\n183\t# Paths to collect during the grace window after offboard — defined per host in host*.conf.\n184\t# HOST1_PARTNERSHIP_MIRROR_BACKUPS / HOST2_PARTNERSHIP_MIRROR_BACKUPS\n185\t# Notified on offboard — no auto-deletion, manual collection.\n186\t\n187\t# Timing — single var controls both Tailscale removal and backup access expiry.\n188\t# Both expire at the same time — keeping backups accessible beyond Tailscale removal is pointless.\n189\t PARTNERSHIP_GRACE_HOURS=6 # hours after offboard before Tailscale removal\n190\t # backup access expires at the same time\n191\t PARTNERSHIP_OFFLINE_THRESHOLD=30 # days either server unreachable before auto-offboard\n192\t # works both directions independently\n193\t\n194\t# Partnership and setup state files — all in STATE_DIR per the project requirement.\n195\t# Scripts use these variables; do not hardcode /boot/config paths in scripts.\n196\t PARTNERSHIP_BLOCKLIST_FILE=\"$STATE_DIR/partnership_blocklist.db\"\n197\t VARAVERK_SETUP_FILE=\"$STATE_DIR/varaverk_setup.db\"\n198\t\n199\t# Tailscale removal on offboard.\n200\t PARTNERSHIP_REMOVE_TAILSCALE=true # remove mirror from Tailscale tailnet on offboard\n201\t # false = skip removal (manual or testing)\n202\t\n203\t# Tailscale API — required when PARTNERSHIP_REMOVE_TAILSCALE=true.\n204\t# Stays in shared conf — only owner uses it, and owner is always running this script.\n205\t# API key: https://login.tailscale.com/admin/settings/keys → Devices write scope\n206\t TAILSCALE_API_KEY=\"\" # tskey-api-...\n207\t TAILSCALE_TAILNET=\"\" # your tailnet name (e.g. yourname.github)\n208\t\n209\t# Transfer safety.\n210\t PARTNERSHIP_TRANSFER_CONFIRM=\"i-understand-this-transfers-ownership\"\n211\t PARTNERSHIP_TRANSFER_STRIKES=3 # consecutive health checks required\n212\t PARTNERSHIP_TRANSFER_MAX_ATTEMPTS=20 # max health check attempts before giving up\n213\t\n214\t# Onboard settings.\n215\t PARTNERSHIP_ONBOARD_VERIFY=true # verify WebUI reachable after reconfiguration\n216\t PARTNERSHIP_ONBOARD_NOTIFY=true # notify both servers on completion\n217\t PARTNERSHIP_SYNC_INTERVAL=15 # minutes — informational, actual schedule in cron\n218\t\n219\t# SSH key lifecycle — managed by Partnership/ssh_setup.sh.\n220\t# Key named after this server: hostname lowercased, unraid- prefix stripped.\n221\t# unRAID-Gmer4Lfe → /root/.ssh/gmer4lfe_rsync_automation\n222\t# Run Partnership/partnership_onboard.sh to generate, copy, and update conf automatically.\n223\t SSH_MAX_STRIKES=5 # consecutive SSH auth failures before critical notify\n224\t SSH_STRIKE_RESET_HRS=24 # hours since last failure before strike counter resets\n225\t\n226\t# FolderView3 plugin integration — chodeus/VladoPortos/scolcipitato.\n227\t# Creates a \"{Partner}-Failover\" folder on onboard, removes + cleans containers on offboard.\n228\t# Folder name derived from remote hostname (strip unraid- prefix case-insensitively).\n229\t# Plugin config: /boot/config/plugins/folder.view3/docker.json\n230\t PARTNERSHIP_FOLDERVIEW3=true # create/remove FolderView3 folder on onboard/offboard\n231\t PARTNERSHIP_FOLDERVIEW3_URL=\"\" # CA plugin URL — leave empty to skip auto-install\n232\t\n233\t# ==============================================================================================\n234\t# ── LOGGING ───────────────────────────────────────────────────────────────────────────────────\n235\t# ==============================================================================================\n236\t\n237\t# Controls verbose [LOG] output across all scripts.\n238\t# true = show detailed [LOG] lines — useful for debugging or first-time setup\n239\t# false = show only user-facing output — cleaner for scheduled runs\n240\t ENABLE_LOGGING=false\n241\t\n242\t# ==============================================================================================\n243\t# ── NOTIFICATIONS ─────────────────────────────────────────────────────────────────────────────\n244\t# ==============================================================================================\n245\t\n246\t# unRAID native notification system — integrates with the bell icon in the WebGUI.\n247\t# normal = job completed successfully / warning = something failed or needs attention\n248\t NOTIFY_UNRAID=true\n249\t\n250\t# Discord webhook URL — defined per host in host*.conf.\n251\t# HOST1_DISCORD_WEBHOOK / HOST2_DISCORD_WEBHOOK\n252\t# Allows different webhooks per server, or only one server notifying.\n253\t\n254\t# ==============================================================================================\n255\t# ── GIT / REPO ────────────────────────────────────────────────────────────────────────────────\n256\t# ==============================================================================================\n257\t\n258\t# Gitea self-hosted repository — used by git_pull_execute.sh.\n259\t# Detects Gitea container location at runtime — works through failover automatically.\n260\t# Falls back to GITEA_DOMAIN if local and Tailscale both fail.\n261\t GITEA_CONTAINER=\"Gitea\"\n262\t GITEA_REPO_PATH=\"FailedProxy/Varaverk.git\"\n263\t GITEA_DOMAIN=\"\" # e.g. git.yourdomain.com — requires NPM + DNS\n264\t TARGET_DIR=\"/boot/config/plugins/varaverk\"\n265\t GITEA_SSH_KEY=\"/root/.ssh/unraid_gitea\"\n266\t SSH_PORT=221 # Gitea SSH port (default 22, Gitea often uses 221/222)\n267\t GITEA_HTTP_PORT=3000 # Gitea web/API port — used by gitea_ssh_setup.sh\n268\t\n269\t# ==============================================================================================\n270\t# ── ORCHESTRATORS ─────────────────────────────────────────────────────────────────────────────\n271\t# ==============================================================================================\n272\t# All orchestrator job lists live here — edit arrays to add/remove scripts.\n273\t# No changes to orchestrator scripts needed when adding or removing jobs.\n274\t# Sections ordered by run frequency: array events first, then shortest interval to longest.\n275\t\n276\t# ━━━ Array Start ━━━\n277\t# Scripts launched by array_started.sh when the array comes online.\n278\t# Launched in order — each as a background process.\n279\t# One-shot scripts (ramdisk, syslog, fpm, inotify, network) run and exit naturally.\n280\t# Continuous scripts (failover) run until array stops.\n281\t# Watchdogs (resource_watchdog, docker_watchdog, system_watchdog) are cronned via\n282\t# watchdog_orchestrator.sh — NOT launched here.\n283\t ARRAY_START_SCRIPTS=(\n284\t \"Plugin/unraid/System_Essentials/unraid_api_key_renew.sh\" # re-register Varaverk API key — registry is ephemeral\n285\t \"System_Essentials/conf_sync.sh\" # pull partner confs + push own conf into /tmp/.vv/ RAM cache\n286\t \"Transcodes/ramdisk_setup.sh\" # creates ramdisk + symlink before Emby starts\n287\t \"System_Essentials/docker_syslog_filter.sh\" # suppress veth noise before logs fill\n288\t \"Plugin/unraid/System_Essentials/php_fpm_max_children.sh\" # WebGUI performance tuning\n289\t \"System_Essentials/inotify_tuning.sh\" # bump inotify limits — containers miss events if exhausted\n290\t \"Docker_Essentials/docker_network_connect.sh\" # ensure networks exist + connect containers\n291\t \"Tools/claude_startup.sh\" # persist Claude data + binary to appdata; re-symlink on boot\n292\t \"Fallback/fallback.sh\" # mutual failover — continuous\n293\t )\n294\t\n295\t# ━━━ Array Stop ━━━\n296\t# Scripts run by array_stopping.sh for a planned shutdown — stops everything cleanly in order.\n297\t# Run sequentially (foreground) — each must complete before the next starts.\n298\t# Order matters: user scripts first (prevents new ops), then data movement, then containers.\n299\t ARRAY_STOP_SCRIPTS=(\n300\t \"Plugin/unraid/System_Essentials/user_scripts_stop.sh\" # stop background scripts before they start new ops\n301\t \"Fallback/fallback.sh --stop\" # gracefully stop fallback (not caught by user_scripts_stop)\n302\t \"System_Essentials/rsync_stop.sh --rsync-only\" # kill rsync; skip container recovery (handled below)\n303\t \"Plugin/unraid/System_Essentials/mover_stop.sh\" # stop mover after rsync (they conflict on same files)\n304\t \"Docker_Essentials/docker_container_stop.sh\" # stop all containers last\n305\t )\n306\t\n307\t# ━━━ Watchdog Orchestrator ━━━\n308\t# watchdog_orchestrator.sh runs WATCHDOG_ORCHESTRATOR_SCRIPTS in order each cron cycle.\n309\t# Schedule: * * * * * (every minute)\n310\t# NOT in ARRAY_START_SCRIPTS — has its own cron entry.\n311\t# Order matters — resource first (frees pressure), docker second (heals with freed resources),\n312\t# system third (storage + webgui component health), stability last (last line of defense).\n313\t WATCHDOG_ORCHESTRATOR_SCRIPTS=(\n314\t \"Watchdogs/resource_watchdog.sh\" # reduce system pressure before healing attempts\n315\t \"Watchdogs/docker_watchdog.sh\" # heal containers with freed resources\n316\t \"Watchdogs/system_watchdog.sh\" # system component health — storage + webgui\n317\t \"Plugin/unraid/System_Essentials/unraid_api_key_renew.sh\" # re-register API key if registry lost it\n318\t \"Watchdogs/stability_watchdog.sh\" # reboot if all else fails — last line of defense\n319\t )\n320\t\n321\t# ━━━ System Watchdog ━━━\n322\t# system_watchdog.sh runs SYSTEM_WATCHDOG_SCRIPTS sequentially each cycle.\n323\t# Called by watchdog_orchestrator.sh — not scheduled directly.\n324\t SYSTEM_WATCHDOG_SCRIPTS=(\n325\t \"Watchdogs/System/storage_watchdog.sh\" # pool growth + runaway log detection\n326\t \"Plugin/unraid/Watchdogs/System/webgui_watchdog.sh\" # WebGUI availability — nginx → php-fpm → emhttp\n327\t \"Watchdogs/System/network_watchdog.sh\" # internet, DDNS, Tailscale, NPM proxy\n328\t )\n329\t\n330\t# ━━━ Critical Sync Maintenance ━━━\n331\t# critical_sync_maintenance.sh runs every 30 minutes.\n332\t# Order: CRITICAL_MAINTENANCE_SCRIPTS (jobs) → CRITICAL_SYNC_SHARES (rsync) → partnership --check\n333\t# partnership --check always runs last regardless of rsync gate.\n334\t# Comment out entries to disable without removing.\n335\t CRITICAL_MAINTENANCE_SCRIPTS=(\n336\t \"Docker_Essentials/downloaders_reset.sh\" # clear stuck download states every 30min\n337\t \"Media/play_state_sync.sh\" # sync watched/resume state across Emby + Jellyfin\n338\t )\n339\t\n340\t# Shares synced every 30 minutes — defined per host in host*.conf.\n341\t# HOST1_CRITICAL_SYNC_SHARES / HOST2_CRITICAL_SYNC_SHARES\n342\t# Format: \"/path/to/share\" or \"/path/to/share|profile-name\"\n343\t# Order matters — Critical-Data first (auth stack), then Emby dirty sync.\n344\t\n345\t# ━━━ Intermediate Sync Maintenance ━━━\n346\t# intermediate_sync_maintenance.sh runs every 4 hours — arr library sync, artwork fetch,\n347\t# and optional mid-day rsync for any shares that need sub-daily propagation.\n348\t# Schedule: 0 */4 * * *\n349\t # INTERMEDIATE_SYNC_SHARES is host-specific — configure HOST*_INTERMEDIATE_SYNC_SHARES in host*.conf.\n350\t\n351\t INTERMEDIATE_MAINTENANCE_SCRIPTS=(\n352\t \"Media/arrs_failed_stalled_recovery.sh\" # blocklist + re-search failed/stalled arr queue items\n353\t )\n354\t # arr_sync.sh runs as a fixed first step in intermediate_sync_maintenance.sh — not listed here.\n355\t # It is controlled by ARR_SYNC_ENABLED (see Arr Sync section above).\n356\t\n357\t# ━━━ Daily Sync Maintenance ━━━\n358\t# daily_sync_maintenance.sh runs media share sync first, then iterates\n359\t# DAILY_MAINTENANCE_SCRIPTS for all jobs.\n360\t# Schedule: 0 1 * * * (1am daily)\n361\t DAILY_MAINTENANCE_SCRIPTS=(\n362\t \"git_pull_execute.sh\" # pull latest scripts — always runs first\n363\t \"Media/media_shares_permissions.sh\" # apply permissions — runs before cleaners\n364\t \"Media/media_cleaner.sh anime\" # remove junk from anime shares\n365\t \"Media/media_cleaner.sh media\" # remove junk from media shares\n366\t #\"Media/lidarr_cleanup.sh\" # remove orphaned music files — enable when ready\n367\t #\"Media/sonarr_cleanup.sh\" # remove orphaned TV files — enable when ready\n368\t \"Media/radarr_cleanup.sh\" # remove orphaned movie files\n369\t \"Media/lidarr_missing_art.sh\" # fetch missing album/artist artwork (HOST1 only — self-guards)\n370\t \"Media/radarr_tmdb_removed.sh\" # remove movies dropped from TMDb\n371\t \"Media/sonarr_tvdb_removed.sh\" # remove series dropped from TVDB\n372\t \"Docker_Essentials/docker_update.sh\" # pull container image updates before restart\n373\t \"Docker_Essentials/docker_daily_restart.sh\" # daily container restarts — runs last\n374\t )\n375\t\n376\t# Pull latest images for DAILY_RESTART_CONTAINERS before the daily restart.\n377\t# Containers keep running during pull — no extra downtime.\n378\t# Set false to skip updates while still running the daily restart.\n379\t DAILY_CONTAINER_UPDATES=true\n380\t\n381\t# Media shares synced daily by daily_sync_maintenance.sh.\n382\t# Defined per-host in host*.conf — HOST1_DAILY_SYNC_SHARES and HOST2_DAILY_SYNC_SHARES.\n383\t# Mesh model: every node pushes every media share. rsync has no --delete so pushes are additive.\n384\t# arr_sync (union) ensures all arr libraries converge first. arr_cleanup removes true orphans.\n385\t# Any node can download content to any share — it propagates to all nodes on the next cycle.\n386\t# Adding HOST3: list every media share in HOST3_DAILY_SYNC_SHARES. No ownership to track.\n387\t# These shares use DEFAULT_RSYNC_OPTS — no profile entry needed.\n388\t# For shares needing custom options or container stops — create a profile in RSYNC section.\n389\t\n390\t# Personal encrypted shares defined per-host in host*.conf.\n391\t# ZFS encrypted at dataset level — remote receives encrypted blocks, cannot read content.\n392\t# See README-Rsync_Setup.md for ZFS encryption setup before uncommenting.\n393\t\n394\t# ━━━ Weekly Sync Maintenance ━━━\n395\t# weekly_sync_maintenance.sh stops containers both sides → pulls updates →\n396\t# syncs WEEKLY_SYNC_SHARES → restarts → then iterates WEEKLY_MAINTENANCE_SCRIPTS.\n397\t# Schedule: 30 2 * * 0 (Sunday 2:30am)\n398\t WEEKLY_MAINTENANCE_SCRIPTS=(\n399\t \"Docker_Essentials/docker_weekly_restart.sh\" # weekly container restarts after sync\n400\t \"Docker_Essentials/docker_update_remaining.sh\" # pull updates for all other containers\n401\t \"System_Essentials/clear_logs.sh\" # purge aged logs — Sunday only, low priority\n402\t \"Media/playback_aware_lidarr_discovery.sh\" # behavior-driven music discovery using weekly Emby playback history\n403\t \"Media/playback_aware_radarr_discovery.sh\" # behavior-driven movie discovery using TMDB recommendations\n404\t \"Media/playback_aware_sonarr_discovery.sh\" # behavior-driven TV discovery using TMDB recommendations\n405\t )\n406\t\n407\t# Pull updates for all running containers NOT in daily/weekly restart lists.\n408\t# Ensures every deployed container receives at least one image pull per week.\n409\t# Set false to skip — docker_weekly_restart.sh still runs regardless.\n410\t WEEKLY_REMAINING_UPDATES=true\n411\t\n412\t# Shares synced during the weekly maintenance window — defined per host in host*.conf.\n413\t# HOST1_WEEKLY_SYNC_SHARES / HOST2_WEEKLY_SYNC_SHARES\n414\t# Containers stopped both sides before sync — full clean state guaranteed.\n415\t# Profiles drive container stops, excludes, and options — configure in RSYNC section.\n416\t# Order matters — Emby first (larger), then Critical-Data (auth stack).\n417\t\n418\t# Container update toggles for the weekly sync window.\n419\t# Containers already stopped for sync — updates pull at no extra downtime.\n420\t# Both false → sync only, no updates.\n421\t WEEKLY_SYNC_UPDATES=true # pull container updates locally during weekly window\n422\t WEEKLY_SYNC_UPDATES_REMOTE=true # pull container updates on remote via SSH\n423\t\n424\t# ━━━ Monthly Maintenance ━━━\n425\t# monthly_maintenance.sh fires only when BOTH gates pass:\n426\t# 1. Server uptime >= MONTHLY_UPTIME_THRESHOLD_DAYS days\n427\t# 2. Last run was >= MONTHLY_RUN_INTERVAL_DAYS days ago (or never run)\n428\t# Cron: 0 3 * * * (daily 3am check — script self-gates, calling daily is safe)\n429\t# NOT in WATCHDOG_ORCHESTRATOR_SCRIPTS — has its own cron entry.\n430\t# Add scripts that require a long-stable settled system — scrubs, extended drive tests.\n431\t# State file on /boot/config — survives reboots (interval gate independent of uptime gate).\n432\t MONTHLY_MAINTENANCE_SCRIPTS=(\n433\t \"Tools/zfs_pool_scrub.sh\" # ZFS pool integrity scrub — all pools, skip ZFS_REPORT_IGNORE_POOLS\n434\t \"Tools/smart_long_test.sh\" # SMART extended self-test — all drives, sequential\n435\t )\n436\t\n437\t MONTHLY_UPTIME_THRESHOLD_DAYS=30 # minimum uptime in days before maintenance fires\n438\t MONTHLY_RUN_INTERVAL_DAYS=30 # minimum days since last run before running again\n439\t MONTHLY_LAST_RUN_FILE=\"$STATE_DIR/monthly_maintenance_last_run.db\"\n440\t\n441\t# ━━━ Sunday Morning Coffee Report ━━━\n442\t# Orchestrator that runs all Sunday monitor scripts in sequence.\n443\t# Schedule: 0 7 * * 0 (Sunday 7am — after weekly_sync_maintenance.sh finishes at ~3am)\n444\t# Each script runs independently and notifies on its own findings.\n445\t COFFEE_REPORT_SCRIPTS=(\n446\t \"Monitors/zfs_memory_snapshot.sh\" # ZFS pool health + ARC + Docker memory snapshot\n447\t \"Monitors/smart_health.sh\" # drive SMART attributes — reallocated, pending, temp\n448\t \"Monitors/cert_monitor.sh\" # SSL certificate expiry for all configured domains\n449\t \"Monitors/backup_verify.sh\" # rsync mirror integrity via independent MD5 checksums\n450\t \"Monitors/bandwidth_monitor.sh\" # weekly rsync transfer totals and per-share breakdown\n451\t \"Monitors/emby_session_report.sh\" # Emby usage — streams, users, library, transcode ratio\n452\t \"Monitors/weekly_health_digest.sh\" # aggregated digest — watchdogs, fallback, skip list\n453\t )\n454\t\n455\t# ==============================================================================================\n456\t# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────\n457\t# ==============================================================================================\n458\t\n459\t# ━━━ Rsync Enable/Disable ━━━\n460\t# Two-tier toggle system — Tier 1 overrides Tier 2.\n461\t#\n462\t# Tier 1 — Global gate:\n463\t# RSYNC_ENABLED=false → ALL rsync stops everywhere, no exceptions\n464\t# Use when: remote completely offline, major maintenance, disaster recovery\n465\t#\n466\t# Tier 2 — Per-orchestrator (only applies when Tier 1 is true):\n467\t# Fine grained control — disable specific orchestrators while keeping others\n468\t# Use when: rebuilding secondary, testing, per-window bandwidth management\n469\t#\n470\t# Example — HOST2 data rebuild:\n471\t# RSYNC_ENABLED=true ← rsync works, individual scripts run fine\n472\t# CRITICAL_RSYNC_ENABLED=true ← 30min auth stack sync still runs\n473\t# INTERMEDIATE_RSYNC_ENABLED=false ← skip 4h arr/mid-day rsync during rebuild\n474\t# DAILY_RSYNC_ENABLED=false ← skip daily HDD syncs during rebuild\n475\t# WEEKLY_RSYNC_ENABLED=true ← Emby + Critical-Data still sync (NVMe)\n476\t# FALLBACK_RSYNC_ENABLED=true ← handback writeback still works\n477\t# → Run individual: bash Rsync/rsync.sh /mnt/user/Movies\n478\t# → When ready: INTERMEDIATE_RSYNC_ENABLED=true DAILY_RSYNC_ENABLED=true\n479\t RSYNC_ENABLED=true # Tier 1 — global gate, overrides everything below\n480\t CRITICAL_RSYNC_ENABLED=true # Tier 2 — disabled during HOST2 rebuild, re-enable when ready\n481\t INTERMEDIATE_RSYNC_ENABLED=true # Tier 2 — intermediate_sync_maintenance.sh rsync section\n482\t DAILY_RSYNC_ENABLED=false # Tier 2 — HOST2 rebuild in progress, re-enable when ready\n483\t WEEKLY_RSYNC_ENABLED=true # Tier 2 — weekly_sync_maintenance.sh rsync section\n484\t FALLBACK_RSYNC_ENABLED=true # Tier 2 — fallback.sh writeback jobs on handback\n485\t\n486\t# ━━━ Rsync Defaults ━━━\n487\t# Global fallback values used when no profile match is found.\n488\t# Media shares in HOST*_DAILY_SYNC_SHARES always use these globals — no profile needed.\n489\t# Appdata shares match profiles by directory basename (lowercased).\n490\t BW_LIMIT=12500 # KB/s — 12500 ≈ 100Mbit\n491\t RETRY_COUNT=3 # retry attempts before giving up\n492\t SLEEP=300 # seconds between retry attempts\n493\t CRITICAL_CONTAINER_NAMES=() # containers stopped on REMOTE before rsync — profiles override\n494\t DELAYED_CONTAINERS=() # containers needing delay before starting — profiles override\n495\t CONTAINER_DELAY=5 # seconds before starting delayed containers\n496\t EXCLUDE_DIRS=() # directories excluded from transfer — profiles override\n497\t\n498\t# --inplace writes directly to destination — delta against existing file, better for large media\n499\t# --partial keep partial file on interrupted transfer so next run resumes, not re-transfers\n500\t# --timeout kill stalled transfers instead of hanging indefinitely\n501\t# --numeric-ids use UIDs/GIDs numerically — prevents ownership mismatches between servers\n502\t# --delete intentionally omitted — arr_cleanup.sh enforces media truth post-rsync.\n503\t# Media shares use this default; rsync spreads files only, never removes them.\n504\t# Note: --no-whole-file removed — redundant over SSH (delta transfer is already the default).\n505\t# Note: arr_cleanup only catches true orphans under the union model — intentional removals\n506\t# require arr_sync.sh --blocklist-add first, then manual file deletion.\n507\t DEFAULT_RSYNC_OPTS=(-av --info=progress2 --human-readable --bwlimit=\"$BW_LIMIT\" --inplace --partial --timeout=60 --numeric-ids)\n508\t\n509\t# ━━━ Remote Health Checks ━━━\n510\t# Pre-flight — aborts if remote rootfs (/) usage is at or above this percentage.\n511\t# When remote array is down, rsync writes land on rootfs and fill it rapidly.\n512\t ROOTFS_WARN=75\n513\t\n514\t# ━━━ Rsync Profile System ━━━\n515\t# Profiles allow per-share rsync behaviour without touching script logic.\n516\t# Profile key matched by basename of directory passed to rsync.sh (lowercased).\n517\t# Override with --profile=name flag.\n518\t#\n519\t# IMPORTANT: PROFILE_RSYNC_OPTS does NOT inherit DEFAULT_RSYNC_OPTS.\n520\t# List ALL desired options explicitly when defining a profile.\n521\t#\n522\t# Current profiles:\n523\t# arrs_stack — arr databases — lower bandwidth, containers stopped for consistency\n524\t# critical-data — auth stack — full stop both sides, Authelia delayed start\n525\t# called by weekly_sync_maintenance.sh — full clean sync weekly\n526\t# critical-fallback — dirty sync — auth stays running both sides, WAL excluded\n527\t# called by critical_sync_maintenance.sh every 30min\n528\t# host1-appdata — HOST1 server-specific appdata — defined in host1.conf\n529\t# host2-appdata — HOST2 server-specific appdata — defined in host2.conf\n530\t# important-data — NextCloud + Postgres — NextCloud delayed start after Postgres\n531\t# emby — weekly clean sync — both Emby stopped, full mirror\n532\t# called by weekly_sync_maintenance.sh only — do NOT schedule separately\n533\t# emby-fallback — dirty sync — Emby stays running, WAL excluded\n534\t# called by critical_sync_maintenance.sh every 30min\n535\t\n536\t declare -A PROFILE_RSYNC_OPTS=(\n537\t [arrs_stack]=\"-av --info=progress2 --human-readable --bwlimit=$BW_LIMIT --delete --inplace\"\n538\t [critical-data]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete\"\n539\t [critical-fallback]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete --inplace --no-whole-file\"\n540\t [important-data]=\"-av --human-readable --bwlimit=$BW_LIMIT\"\n541\t [emby]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete --inplace --no-whole-file\"\n542\t [emby-fallback]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete --inplace --no-whole-file\"\n543\t )\n544\t\n545\t# Per-profile bandwidth limits in KB/s\n546\t declare -A PROFILE_BW_LIMIT=(\n547\t [arrs_stack]=5000 # lower — runs alongside other syncs\n548\t [critical-data]=9500 # high — small dataset, sync fast\n549\t [critical-fallback]=9500 # high — small dataset, sync fast\n550\t [important-data]=9500 # high — database sync\n551\t [emby]=8000 # medium — large full mirror\n552\t [emby-fallback]=9500 # high — small critical dataset\n553\t )\n554\t\n555\t# Retry attempts per profile\n556\t declare -A PROFILE_RETRY_COUNT=(\n557\t [arrs_stack]=3\n558\t [critical-data]=3\n559\t [critical-fallback]=3\n560\t [important-data]=3\n561\t [emby]=3\n562\t [emby-fallback]=3\n563\t )\n564\t\n565\t# Seconds between retry attempts\n566\t declare -A PROFILE_SLEEP=(\n567\t [arrs_stack]=300\n568\t [critical-data]=300\n569\t [critical-fallback]=120 # shorter — frequent dirty sync, retry faster\n570\t [important-data]=300\n571\t [emby]=300\n572\t [emby-fallback]=120 # shorter — frequent dirty sync, retry faster\n573\t )\n574\t\n575\t# Containers stopped on BOTH LOCAL and REMOTE before rsync.\n576\t# Local stops first — flushes databases cleanly. Remote stops next — prevents writes.\n577\t# Only running containers get restarted — stopped containers stay stopped.\n578\t# SPACE-SEPARATED STRINGS — converted to array at runtime\n579\t declare -A PROFILE_CRITICAL_CONTAINER_NAMES=(\n580\t [arrs_stack]=\"Sonarr Lidarr Readarr Radarr Prowlarr Bazarr Pinchflat\"\n581\t [critical-data]=\"Mariadb-Authelia Mariadb-Authelia-Secondary Redis-Authelia Redis-Authelia-Secondary Lldap-Gmer4Lfe NginxProxyManager Authelia Authelia-Secondary\"\n582\t [critical-fallback]=\"\" # dirty sync — auth stays running both sides\n583\t [important-data]=\"Postgres-NextCloud NextCloud\"\n584\t [emby]=\"Emby\"\n585\t [emby-fallback]=\"\" # dirty sync — Emby stays running both sides\n586\t )\n587\t\n588\t# Containers needing a delay after rsync before starting.\n589\t# SPACE-SEPARATED STRINGS — converted to array at runtime\n590\t declare -A PROFILE_DELAYED_CONTAINERS=(\n591\t [arrs_stack]=\"\"\n592\t [critical-data]=\"Authelia Authelia-Secondary\" # wait for Mariadb + Redis\n593\t [critical-fallback]=\"\"\n594\t [important-data]=\"NextCloud\" # wait for Postgres\n595\t [emby]=\"\"\n596\t [emby-fallback]=\"\"\n597\t )\n598\t\n599\t# Seconds before starting delayed containers\n600\t declare -A PROFILE_CONTAINER_DELAY=(\n601\t [arrs_stack]=5\n602\t [critical-data]=15 # Mariadb + Redis need time to accept connections\n603\t [critical-fallback]=5\n604\t [important-data]=10 # Postgres needs time before NextCloud\n605\t [emby]=5\n606\t [emby-fallback]=5\n607\t )\n608\t\n609\t# Directories excluded from rsync per profile.\n610\t# SPACE-SEPARATED STRINGS — converted to array at runtime\n611\t declare -A PROFILE_EXCLUDE_DIRS=(\n612\t [arrs_stack]=\"logs *.tmp\"\n613\t [critical-data]=\"logs *.tmp *.log nginx/temp nginx/cache __pycache__ notification.txt\"\n614\t [critical-fallback]=\"logs *.tmp *.log nginx/temp nginx/cache __pycache__ notification.txt *.db-wal *.db-shm\"\n615\t [important-data]=\"logs *.tmp\"\n616\t [emby]=\"logs transcodes cache crash*\"\n617\t [emby-fallback]=\"logs transcodes cache metadata *.db-wal *.db-shm crash* plugins root\"\n618\t )\n619\t\n620\t# Remote restart after dirty sync — restart these on remote IF they were running before sync.\n621\t# Same logic as stop/start — was stopped = stays stopped, was running = gets restarted.\n622\t# Used by dirty sync profiles (critical-fallback, emby-fallback) so remote picks up changes.\n623\t# SPACE-SEPARATED STRINGS — converted to array at runtime\n624\t declare -A PROFILE_REMOTE_RESTART_CONTAINERS=(\n625\t [critical-fallback]=\"NginxProxyManager Authelia Authelia-Secondary Lldap-Gmer4Lfe Mariadb-Authelia Mariadb-Authelia-Secondary Redis-Authelia Redis-Authelia-Secondary\"\n626\t [emby-fallback]=\"Emby\"\n627\t )\n628\t\n629\t# Note: disk check is auto-detected from disks.ini — no PROFILE_SKIP_DISK_CHECK needed.\n630\t# check_remote_disks() reads fsType per disk and handles XFS, ZFS, and cache pools automatically.\n631\t\n632\t# ==============================================================================================\n633\t# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────\n634\t# ==============================================================================================\n635\t# Mutual container failover between two unRAID servers.\n636\t# Each server runs Fallback/fallback.sh independently via array_started.sh.\n637\t# All decisions based on two pings: remote reachable + internet reachable.\n638\t#\n639\t# States: NORMAL | FALLBACK | NO_INTERNET | DARK\n640\t#\n641\t# DDNS rules — absolute:\n642\t# Internet loss → stop own DDNS immediately\n643\t# Failover → start remote DDNS first (Tier 1)\n644\t# Handback → stop remote DDNS → rsync → start containers → start local DDNS last\n645\t#\n646\t# Per-host container lists and tier delays live in host*.conf.\n647\t# Shared settings (intervals, state file, thresholds) live here.\n648\t\n649\t EXTERNAL_IP=\"8.8.8.8\"\n650\t FALLBACK_CHECK_INTERVAL=30 # seconds between fallback state checks\n651\t FALLBACK_HANDBACK_STRIKES=3 # consecutive healthy checks before initiating handback (3×30s = 90s)\n652\t FALLBACK_STATE_FILE=\"$STATE_DIR/fallback_state.db\"\n653\t FALLBACK_ENABLED=true # HOST2 back online\n654\t # false = suppresses \"not running\" warnings in status scripts\n655\t FALLBACK_PARTNERSHIP_REQUIRED=true # gate fallback on an active partnership\n656\t # false = standalone mode, no partnership dependency\n657\t FALLBACK_PARTNERSHIP_SUSPEND_AFTER=120 # minutes without active partnership before suspending\n658\t # 0 = suspend immediately when partnership goes inactive\n659\t\n660\t# ━━━ Failover Test ━━━\n661\t# Controlled simulation of a failover event — run manually via fallback_test.sh.\n662\t FALLBACK_TEST_BLOCK_WAIT=150 # seconds to wait after blocking connectivity\n663\t FALLBACK_TEST_HANDBACK_WAIT=360 # seconds to wait before initiating handback\n664\t\n665\t# ==============================================================================================\n666\t# ── DOCKER ESSENTIALS ─────────────────────────────────────────────────────────────────────────\n667\t# ==============================================================================================\n668\t\n669\t# ━━━ Downloaders Reset ━━━\n670\t# Runs every 30 minutes via CRITICAL_MAINTENANCE_SCRIPTS.\n671\t# Clears stuck states, purges old history, prepares each download client for a clean cycle.\n672\t# Per-host URLs and API keys live in host*.conf.\n673\t DOWNLOADER_RETENTION_DAYS=7 # days — purge history older than this\n674\t\n675\t# qBittorrent failsafe — removes torrents older than threshold regardless of ratio\n676\t# deleteFiles=false — removes from qBit but leaves files on disk (arr manages files)\n677\t QBIT_FAILSAFE_MIN_DAYS=180 # days — minimum age before failsafe deletion\n678\t QBIT_FAILSAFE_MIN_RATIO=0 # 0 = age only, no ratio requirement\n679\t\n680\t# ━━━ Docker Daily Restart ━━━\n681\t# Containers restarted every day via DAILY_MAINTENANCE_SCRIPTS.\n682\t# Per-host lists live in host*.conf:\n683\t# HOST1_DAILY_RESTART_CONTAINERS\n684\t# HOST2_DAILY_RESTART_CONTAINERS\n685\t# detect_hosts() sets DAILY_RESTART_CONTAINERS to the correct host array at runtime.\n686\t\n687\t# ━━━ Docker Weekly Restart ━━━\n688\t# Less critical services restarted weekly via WEEKLY_MAINTENANCE_SCRIPTS (Sunday 2:30am).\n689\t# Containers already stopped for weekly sync — restart adds zero extra downtime.\n690\t# Per-host lists live in host*.conf:\n691\t# HOST1_WEEKLY_RESTART_CONTAINERS\n692\t# HOST2_WEEKLY_RESTART_CONTAINERS\n693\t\n694\t# ━━━ Docker Watchdog ━━━\n695\t# Continuous two-tier self-healing container monitoring.\n696\t# Started by array_started.sh — runs until array stops.\n697\t# Re-sources all three conf files each cycle — add/remove containers without restarting watchdog.\n698\t#\n699\t# Tier 1 — strict monitoring of explicitly configured containers:\n700\t# Memory hard limits — immediate restart if exceeded\n701\t# CPU thresholds — strike system, restart after CPU_FAIL_LIMIT sustained strikes\n702\t# HTTP responsiveness — strike system, restart after RESP_FAIL_LIMIT failed checks\n703\t# Required containers — must always be running, strike + skip list with auto-clear\n704\t#\n705\t# Tier 2 — global health scan of ALL running containers:\n706\t# Unhealthy status — Docker HEALTHCHECK unhealthy → restart\n707\t# OOM killed — kernel killed → restart + notify\n708\t# Crash loop detection — RestartCount climbing → notify, critical above limit\n709\t# Dead containers — remove and restart\n710\t# Unexpected exits — non-zero exit code → restart\n711\t#\n712\t# All per-host container lists live in host*.conf:\n713\t# HOST*_WATCHDOG_CONTAINERS — memory hard limits per container\n714\t# HOST*_WATCHDOG_CONTAINER_URLS — HTTP health check URLs\n715\t# HOST*_WATCHDOG_REQUIRED_CONTAINERS — must always be running\n716\t# HOST*_WATCHDOG_SCAN_IGNORE — skip in Tier 2 scan\n717\t# HOST*_WATCHDOG_DEPENDENCIES — dependency ordering for restart decisions\n718\t\n719\t# Strike state file — persistent in STATE_DIR\n720\t WATCHDOG_STATE_FILE=\"$STATE_DIR/container_watchdog_state.db\"\n721\t\n722\t# CPU thresholds — normalised against total core count at runtime\n723\t SOFT_CPU_THRESHOLD=80 # warn at this % of total system CPU\n724\t HARD_CPU_THRESHOLD=85 # strike at this % of total system CPU\n725\t CPU_FAIL_LIMIT=2 # consecutive hard CPU strikes before container restart\n726\t\n727\t# Memory soft threshold — warn when container reaches this % of its hard limit\n728\t SOFT_MEM_THRESHOLD=80\n729\t\n730\t# HTTP responsiveness\n731\t RESP_FAIL_LIMIT=2 # consecutive failed checks before restart\n732\t CURL_TIMEOUT=5 # seconds per check before timeout\n733\t\n734\t# Tier 2 master toggle\n735\t WATCHDOG_SCAN_ALL=true # false = only WATCHDOG_CONTAINERS + required containers\n736\t\n737\t# Individual Tier 2 check toggles\n738\t WATCHDOG_RESTART_UNHEALTHY=true\n739\t WATCHDOG_RESTART_DEAD=true\n740\t WATCHDOG_RESTART_CRASHED=true\n741\t WATCHDOG_NOTIFY_OOM=true\n742\t WATCHDOG_NOTIFY_CRASHLOOP=true\n743\t\n744\t# Crash loop threshold\n745\t WATCHDOG_CRASH_LIMIT=5 # RestartCount above this = critical crash loop\n746\t\n747\t# Startup grace period — skip restarts while system is still booting\n748\t WATCHDOG_STARTUP_GRACE=600 # seconds after boot before watchdog acts on failures\n749\t\n750\t# Restart loop protection — prevents watchdog from endlessly restarting a broken container\n751\t WATCHDOG_CONTAINER_RESTART_LIMIT=3\n752\t WATCHDOG_CONTAINER_RESTART_WINDOW=1 # rolling window in hours\n753\t WATCHDOG_CONTAINER_RESTART_LOG=\"$DATA_DIR/container_restart_history.db\"\n754\t\n755\t# Notification batching — one summary per cycle instead of one ping per event\n756\t WATCHDOG_BATCH_NOTIFY=true\n757\t\n758\t# Appdata size monitoring — two-part catch-all for runaway growth and oversized log files.\n759\t#\n760\t# Part 1 — Growth rate (zero-config):\n761\t# Reads per-container dir totals each cycle via du, compares to previous cycle.\n762\t# Any container growing more than WATCHDOG_APPDATA_GROWTH_GB triggers a focused *.log scan\n763\t# inside that container. No per-container config required — new containers are covered\n764\t# automatically. Baseline built on first run after boot; growth detection starts cycle 2.\n765\t#\n766\t# Part 2 — Absolute log size:\n767\t# Finds *.log / *.log.* files over WATCHDOG_APPDATA_LOG_MAX_GB across all appdata paths.\n768\t# Catches logs that have already stabilised at a large size and are no longer actively growing.\n769\t#\n770\t# Strike system (reuses existing watchdog infrastructure):\n771\t# Strike 1 — warn + notify: condition first detected\n772\t# Strike 2 — warn + escalated notify: still present next cycle\n773\t# Strike 3 (WATCHDOG_APPDATA_STRIKE_LIMIT) — action cycle:\n774\t# If WATCHDOG_APPDATA_TRUNCATE_LOGS=true: truncate *.log files in-place, clear strikes\n775\t# If false: critical notify only, strikes held until condition resolves\n776\t# Condition resolves (growth stops / log drops below threshold) → strikes auto-clear\n777\t#\n778\t# HOST*_WATCHDOG_APPDATA_SIZES (in host*.conf) suppresses growth warnings for a\n779\t# container until its dir exceeds the configured ceiling. Only needed when a container\n780\t# legitimately has large stable data and you want to guarantee it never triggers a false alarm.\n781\t WATCHDOG_CHECK_APPDATA=true\n782\t WATCHDOG_APPDATA_PATHS=(\"/mnt/docker-unraid/appdata\")\n783\t WATCHDOG_APPDATA_GROWTH_GB=2 # flag containers growing more than this per cycle\n784\t WATCHDOG_APPDATA_LOG_MAX_GB=2 # flag *.log files exceeding this size (absolute)\n785\t WATCHDOG_APPDATA_TRUNCATE_LOGS=false # set true to auto-truncate oversized *.log files on action cycle\n786\t WATCHDOG_APPDATA_STRIKE_LIMIT=3 # cycles before action fires (matches existing watchdog pattern)\n787\t WATCHDOG_APPDATA_GROWTH_FILE=\"$STATE_DIR/watchdog_appdata_growth.db\"\n788\t STORAGE_WATCHDOG_STATE_FILE=\"$STATE_DIR/storage_watchdog_state.db\"\n789\t\n790\t# ━━━ Docker Network Connect ━━━\n791\t# Ensures custom networks exist and connects containers at array start.\n792\t# Runs once via ARRAY_START_SCRIPTS — idempotent, safe to re-run.\n793\t# Container and network lists are host-specific — defined in host*.conf:\n794\t# HOST1_NETWORK_CONNECT_CONTAINERS / HOST2_NETWORK_CONNECT_CONTAINERS\n795\t# HOST1_NETWORK_CONNECT_NETWORKS / HOST2_NETWORK_CONNECT_NETWORKS\n796\t\n797\t# ==============================================================================================\n798\t# ── UNRAID ESSENTIALS ─────────────────────────────────────────────────────────────────────────\n799\t# ==============================================================================================\n800\t\n801\t# ━━━ inotify Tuning ━━━\n802\t# Linux inotify limits — applied at every array start by inotify_tuning.sh.\n803\t# Default unRAID values are very low — with many Docker containers watching files\n804\t# you can silently exhaust the limit causing containers to miss file events.\n805\t# Symptoms: containers miss file events, downloads not detected, library not updated.\n806\t# Live TV stutter is a known symptom of inotify exhaustion.\n807\t# These settings are lost on reboot — reapplied automatically at array start.\n808\t INOTIFY_MAX_INSTANCES=1024 # default: 128 — max inotify instances per user\n809\t # 1024 handles ~20-30 containers watching files simultaneously\n810\t INOTIFY_MAX_WATCHES=1048576 # default: 8192 — SHARED budget across ALL users/containers\n811\t # 524288 (512K) was previous value — raised to 1M (1048576)\n812\t # VSCode/Code-Server alone needs ~50K-200K for large workspaces\n813\t # with node_modules. All arr containers + Emby + VSCode share\n814\t # this budget. 1M is safe on 128GB RAM (~128MB kernel memory)\n815\t # If VSCode shows \"unable to watch for file changes\" → too low\n816\t INOTIFY_MAX_QUEUED_EVENTS=32768 # default: 16384 — max events queued before dropping\n817\t\n818\t# ━━━ System Tuning Monitor ━━━\n819\t# Tracks inotify and php-fpm usage over time.\n820\t# Snapshot written every 6 hours by system_tuning_monitor.sh.\n821\t# Read by sunday_morning_coffee_report.sh for weekly peak/avg/warning summary.\n822\t INOTIFY_WARN_PCT=80 # warn if inotify instances exceed this % of limit\n823\t PHP_FPM_WARN_PCT=80 # warn if php-fpm workers exceed this % of max_children\n824\t TUNING_MONITOR_LOG=\"$DATA_DIR/system_tuning_history.db\"\n825\t TUNING_LOG_RETENTION=30 # days before old entries are purged\n826\t\n827\t# ━━━ Reboot ━━━\n828\t# Seconds of warning broadcast to logged-in users before server_reboot.sh reboots.\n829\t# Gives users time to save work — 300s = 5 minutes.\n830\t REBOOT_SLEEP=300\n831\t\n832\t# ━━━ Mover ━━━\n833\t# Seconds to wait before mover_stop.sh sends SIGTERM to the mover process.\n834\t# Gives mover time to finish current file transfer before being interrupted.\n835\t MOVER_STOP_TIMEOUT=300\n836\t\n837\t# ━━━ Syslog Filter ━━━\n838\t# Path for the rsyslog filter file that suppresses Docker veth interface noise.\n839\t# Docker creates a new veth interface for each container — generates hundreds of\n840\t# log lines per hour that have no diagnostic value. Filter removes them at source.\n841\t FILTER_FILE=\"/etc/rsyslog.d/ignore-docker-veth.conf\"\n842\t\n843\t# ━━━ PHP-FPM ━━━\n844\t# Higher max_children allows more concurrent PHP requests to the unRAID WebGUI.\n845\t# Default is very low — increasing it prevents WebGUI slowdowns under load.\n846\t# 250 is safe for servers with 32GB+ RAM.\n847\t PHP_CONF=\"/etc/php-fpm.d/www.conf\"\n848\t PHP_MAX_CHILDREN=250\n849\t\n850\t# ━━━ Clear Logs ━━━\n851\t# System log files cleared weekly to prevent rootfs fill over time.\n852\t# These grow continuously — without clearing they eventually consume all rootfs space.\n853\t# clear_logs.sh runs in WEEKLY_MAINTENANCE_SCRIPTS — Sunday 2:30am.\n854\t#\n855\t# Size threshold approach — only clear if log exceeds threshold.\n856\t# Avoids destroying useful recent diagnostic context when logs are small.\n857\t# LOG_MIN_SIZE_MB: skip clearing if log is under this size (not worth clearing)\n858\t# LOG_DOCKER_MAX_MB: clear a container log only if it exceeds this size\n859\t# Docker logs grow fastest on active containers (Emby, SABnzbd, Sonarr)\n860\t# 100MB per container × 30 containers = 3GB before clearing kicks in\n861\t LOG_FILES=(/var/log/syslog /var/log/messages /var/log/dmesg)\n862\t LOG_MIN_SIZE_MB=10 # skip system log if under this size (already small)\n863\t LOG_DOCKER_MAX_MB=100 # clear Docker container log only if over this size (MB)\n864\t\n865\t# ━━━ Network Watchdog ━━━\n866\t# Services-layer connectivity — internet reachability, DDNS sync, Tailscale, NPM proxy.\n867\t# Host-specific values (domain, container, NPM URL) live in host*.conf.\n868\t NETWORK_WATCHDOG_ENABLED=true\n869\t NETWORK_WATCHDOG_INTERNET_URL=\"https://1.1.1.1\"\n870\t NETWORK_WATCHDOG_INTERNET_TIMEOUT=5\n871\t NETWORK_WATCHDOG_CHECK_TAILSCALE=true\n872\t NETWORK_WATCHDOG_NPM_TIMEOUT=10\n873\t NETWORK_WATCHDOG_NPM_STRIKE_LIMIT=2\n874\t NETWORK_WATCHDOG_NPM_STATE_FILE=\"$STATE_DIR/network_watchdog_state.db\"\n875\t\n876\t# ━━━ WebGUI Watchdog ━━━\n877\t# Monitors unRAID WebGUI responsiveness — escalates through nginx restart → emhttp restart.\n878\t# Separate from docker_watchdog — this monitors the unRAID UI itself, not containers.\n879\t WEBGUI_URL=\"http://localhost\"\n880\t WEBGUI_TIMEOUT=5 # seconds before curl gives up on WebGUI check\n881\t WEBGUI_NGINX_WAIT=15 # seconds after nginx restart before rechecking\n882\t WEBGUI_EMHTTP_WAIT=30 # seconds after emhttp restart before rechecking\n883\t\n884\t# ==============================================================================================\n885\t# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────\n886\t# ==============================================================================================\n887\t\n888\t# ━━━ Media Permissions ━━━\n889\t# Applied recursively by media_shares_permissions.sh.\n890\t# Runs first in DAILY_MAINTENANCE_SCRIPTS — arr cleanup depends on correct ownership.\n891\t#\n892\t# Why split directory vs file permissions:\n893\t# Directories need execute bit to enter — 755 allows owner+group+others to traverse\n894\t# Files should NOT be executable — 664 allows owner+group read/write, others read\n895\t# This is POSIX best practice for media servers, not a blanket 777 band-aid\n896\t#\n897\t# Why nobody:users ownership:\n898\t# All ecosystem containers run as PUID=99 (nobody) PGID=100 (users) on unRAID\n899\t# Files owned by nobody:users are accessible to all containers without world-write\n900\t# If this script fixes many files each run → a container has wrong PUID/PGID set\n901\t# Fix: add PUID=99 PGID=100 to the container's environment variables\n902\t#\n903\t# This script runs daily as a failsafe — even with correct container config:\n904\t# rsync may bring files with source ownership if not run with --chown\n905\t# Manual admin copies create root:root files\n906\t# New containers may not have PUID/PGID set yet\n907\t PERMISSIONS_DIR_MODE=\"755\" # directories — traverse + list, no world-write\n908\t PERMISSIONS_FILE_MODE=\"664\" # files — group read/write, no execute\n909\t PERMISSIONS_OWNER=\"nobody:users\"\n910\t\n911\t# Share list defined per host in host*.conf:\n912\t# HOST1_MEDIA_PERMISSION_SHARES / HOST2_MEDIA_PERMISSION_SHARES\n913\t\n914\t# ━━━ Media Cleaner ━━━\n915\t# Removes junk files from media shares — two profiles: anime and media.\n916\t# Called via DAILY_MAINTENANCE_SCRIPTS. Run manually: Media/media_cleaner.sh anime|media\n917\t# Folder lists defined per host in host*.conf:\n918\t# HOST1_ANIME_CLEAN_FOLDERS / HOST2_ANIME_CLEAN_FOLDERS\n919\t# HOST1_MEDIA_CLEAN_FOLDERS / HOST2_MEDIA_CLEAN_FOLDERS\n920\t\n921\t ANIME_FILE_PATTERNS=(\n922\t # ── Checksums ────────────────────────────────────────────────────────────────────────\n923\t '*.sfv' '*.md5' '*.sha1' # verification files — useless post-download\n924\t\n925\t # ── Scene / download metadata ─────────────────────────────────────────────────────────\n926\t '*.url' '*.lnk' # scene links\n927\t '*.info' '*.diz' # scene description files\n928\t '*.nzb' # usenet download files\n929\t '*.torrent' # torrent files left by download clients\n930\t\n931\t # ── Archives and segments ─────────────────────────────────────────────────────────────\n932\t '*.rar' '*.zip' '*.7z' '*.ace' # archives — source files not needed after extract\n933\t '*.r00' '*.r01' '*.r02' '*.r03' # multi-part rar segments\n934\t '*.r04' '*.r05' '*.r06' '*.r07'\n935\t '*.r08' '*.r09' '*.srr' # scene repair files\n936\t '*.001' '*.002' '*.003' # split archive parts\n937\t '*.gz' '*.tar' '*.bz2' # linux archives\n938\t\n939\t # ── Scene samples and proofs ──────────────────────────────────────────────────────────\n940\t '*.sample*' '*.proof*' # scene samples — never needed in library\n941\t\n942\t # ── Executables and scripts ───────────────────────────────────────────────────────────\n943\t '*.exe' '*.scr' '*.com' # Windows executables\n944\t '*.bat' '*.cmd' '*.vbs' '*.ps1' # Windows scripts\n945\t '*.msi' '*.dll' '*.sys' # Windows installers and system files\n946\t '*.sh' # shell scripts in media folders = suspicious\n947\t\n948\t # ── Incomplete downloads ──────────────────────────────────────────────────────────────\n949\t '*.!ut' '*.!qB' # uTorrent / qBittorrent incomplete markers\n950\t '*.crdownload' '*.opdownload' # Chrome / Opera incomplete downloads\n951\t '*.part' # partial download files\n952\t\n953\t # ── Sync conflicts ────────────────────────────────────────────────────────────────────\n954\t '*sync-conflict*' # Syncthing conflict copies\n955\t )\n956\t\n957\t MEDIA_FILE_PATTERNS=(\n958\t # ── Checksums ────────────────────────────────────────────────────────────────────────\n959\t '*.sfv' '*.md5' '*.sha1' # verification files — useless post-download\n960\t\n961\t # ── Scene / download metadata ─────────────────────────────────────────────────────────\n962\t '*.url' '*.lnk' # scene links\n963\t '*.info' '*.diz' # scene description files\n964\t '*.nzb' # usenet download files\n965\t '*.torrent' # torrent files left by download clients\n966\t\n967\t # ── Archives and segments ─────────────────────────────────────────────────────────────\n968\t '*.rar' '*.zip' '*.7z' '*.ace' # archives — source files not needed after extract\n969\t '*.r00' '*.r01' '*.r02' '*.r03' # multi-part rar segments\n970\t '*.r04' '*.r05' '*.r06' '*.r07'\n971\t '*.r08' '*.r09' '*.srr' # scene repair files\n972\t '*.001' '*.002' '*.003' # split archive parts\n973\t '*.gz' '*.tar' '*.bz2' # linux archives\n974\t\n975\t # ── Scene samples and proofs ──────────────────────────────────────────────────────────\n976\t '*.sample*' '*.proof*' # scene samples — never needed in library\n977\t\n978\t # ── Executables and scripts ───────────────────────────────────────────────────────────\n979\t '*.exe' '*.scr' '*.com' # Windows executables\n980\t '*.bat' '*.cmd' '*.vbs' '*.ps1' # Windows scripts\n981\t '*.msi' '*.dll' '*.sys' # Windows installers and system files\n982\t '*.sh' # shell scripts in media folders = suspicious\n983\t\n984\t # ── Incomplete downloads ──────────────────────────────────────────────────────────────\n985\t '*.!ut' '*.!qB' # uTorrent / qBittorrent incomplete markers\n986\t '*.crdownload' '*.opdownload' # Chrome / Opera incomplete downloads\n987\t '*.part' # partial download files\n988\t\n989\t # ── Sync conflicts ────────────────────────────────────────────────────────────────────\n990\t '*sync-conflict*' # Syncthing conflict copies\n991\t\n992\t # ── Media-specific extras ─────────────────────────────────────────────────────────────\n993\t '*.iso' # disc images — extracted content is in library\n994\t '*.lrc' # lyrics files — handled by music apps not Emby\n995\t )\n996\t\n997\t# ━━━ Arr Cleanup ━━━\n998\t# Orphan file cleanup via Lidarr, Sonarr, and Radarr APIs.\n999\t# Per-host URLs, API keys, and path maps live in host*.conf.\n1000\t# detect_hosts() selects correct host vars at runtime.\n1001\t#\n1002\t# API versions — update MAJOR version here when script is updated for a new arr version:\n1003\t# Sonarr v4 → /api/v3/series → /api/v3/episodefile?seriesId=X\n1004\t# Radarr v6 → /api/v3/movie → /api/v3/moviefile?movieId=X\n1005\t# Lidarr v3 → /api/v1/artist → /api/v1/trackFile?artistId=X\n1006\t SONARR_VERSION_MAJOR=4\n1007\t RADARR_VERSION_MAJOR=6\n1008\t LIDARR_VERSION_MAJOR=3\n1009\t\n1010\t# Lidarr shared settings\n1011\t LIDARR_LOCK_WARN_AGE=3600 # 1hr — large libraries take time, not stuck\n1012\t LIDARR_ORPHAN_AGE=7 # days — files must be older than this before eligible for deletion\n1013\t LIDARR_MAX_DELETE_GB=5 # require --i-know-what-im-doing if deletion exceeds this\n1014\t LIDARR_MIN_TRACKED_PCT=80 # abort if tracked count drops below this % of last run\n1015\t # protects against API returning partial data on a bad day\n1016\t LIDARR_TRACKED_COUNT_FILE=\"$DATA_DIR/lidarr_tracked.count\"\n1017\t LIDARR_EXTENSIONS=(\"flac\" \"mp3\" \"m4a\" \"wav\" \"aac\" \"ogg\" \"opus\" \"wma\")\n1018\t LIDARR_PROTECTED_PATTERNS=(\n1019\t # Metadata\n1020\t \"*.nfo\" \"*.tbn\"\n1021\t # Images — album art, artist images, Emby artwork\n1022\t \"*.jpg\" \"*.jpeg\" \"*.png\" \"*.webp\" \"*.svg\"\n1023\t \"poster.*\" \"fanart.*\" \"backdrop.*\" \"clearlogo.*\"\n1024\t \"banner.*\" \"thumb.*\" \"landscape.*\"\n1025\t \"folder.*\" \"cover.*\" \"album.*\" \"artist.*\" \"disc.*\"\n1026\t # Lyrics\n1027\t \"*.lrc\"\n1028\t )\n1029\t\n1030\t# Lidarr artwork fetcher settings\n1031\t LIDARR_ART_MIN_SIZE=10000 # bytes — reject downloads smaller than this\n1032\t LIDARR_ART_MAX_PARALLEL=4 # concurrent background download jobs\n1033\t LIDARR_ART_RETRIES=2 # download retry attempts per image\n1034\t LIDARR_ART_SLEEP_BETWEEN=0.2 # seconds between fanart.tv API calls\n1035\t# HOST*_FANART_API_KEY / HOST*_LASTFM_API_KEY — set in host*.conf\n1036\t\n1037\t# Lidarr discovery settings (playback_aware_lidarr_discovery.sh)\n1038\t LIDARR_DISCOVERY_THRESHOLD=70 # score to accept candidate (0-100)\n1039\t LIDARR_DISCOVERY_LOOKBACK_DAYS=7 # Emby play history window in days\n1040\t LIDARR_DISCOVERY_MIN_PLAYS=3 # min plays in window before evaluating an artist\n1041\t LIDARR_DISCOVERY_USER_CAP_PCT=35 # max % any single user can contribute to play score (prevents one listener dominating)\n1042\t LIDARR_DISCOVERY_MAX_ADDS=5 # max artists to add per run — quality over bulk\n1043\t LIDARR_DISCOVERY_REJECT_COOLDOWN=30 # days before re-evaluating a rejected artist\n1044\t LIDARR_DISCOVERY_HISTORY=\"$DATA_DIR/lidarr_discovery_history.db\"\n1045\t\n1046\t# Sonarr discovery settings (playback_aware_sonarr_discovery.sh)\n1047\t SONARR_DISCOVERY_THRESHOLD=52 # score to accept candidate (0-100)\n1048\t SONARR_DISCOVERY_LOOKBACK_DAYS=14 # Emby episode play history window in days (shorter than movies — TV watched more frequently)\n1049\t SONARR_DISCOVERY_MAX_SEEDS=5 # max seed series from Stage 1\n1050\t SONARR_DISCOVERY_MAX_ADDS=3 # max shows to add per run — TV is a larger commitment than movies\n1051\t SONARR_DISCOVERY_MIN_VOTE_COUNT=50 # min TMDB votes (TV has fewer votes than movies at same popularity)\n1052\t SONARR_DISCOVERY_MIN_RATING=65 # min TMDB vote_average × 10 (65 = 6.5/10)\n1053\t SONARR_DISCOVERY_REJECT_COOLDOWN=60 # days before re-evaluating a rejected show\n1054\t SONARR_DISCOVERY_USER_EPISODE_CAP=8 # max episodes any one user contributes to seed volume score\n1055\t SONARR_DISCOVERY_MONITOR_MODE=\"all\" # Sonarr monitor mode on add: all | future | first | latest | none\n1056\t SONARR_DISCOVERY_HISTORY=\"$DATA_DIR/sonarr_discovery_history.db\"\n1057\t\n1058\t# Radarr discovery shared settings\n1059\t RADARR_DISCOVERY_THRESHOLD=52 # score to accept candidate (0-100) — lower than Lidarr since diverse seeds rarely overlap\n1060\t RADARR_DISCOVERY_LOOKBACK_DAYS=30 # Emby watch history window in days (movies rewatched less often)\n1061\t RADARR_DISCOVERY_MAX_SEEDS=5 # max seed movies from Stage 1\n1062\t RADARR_DISCOVERY_MAX_ADDS=5 # max movies to add per run\n1063\t RADARR_DISCOVERY_MIN_VOTE_COUNT=100 # min TMDB votes to be considered a candidate\n1064\t RADARR_DISCOVERY_MIN_RATING=60 # min TMDB vote_average × 10 (60 = 6.0/10)\n1065\t RADARR_DISCOVERY_REJECT_COOLDOWN=60 # days before re-evaluating a rejected movie\n1066\t RADARR_DISCOVERY_SEED_LIBRARIES=(\"Movies\") # Emby libraries to draw seed movies from\n1067\t RADARR_DISCOVERY_HISTORY=\"$DATA_DIR/radarr_discovery_history.db\"\n1068\t\n1069\t# Emby → arr sync library allowlists\n1070\t# Only these Emby library names will be considered by the sync tools.\n1071\t# Names must match exactly as shown in Emby > Dashboard > Libraries.\n1072\t SONARR_EMBY_LIBRARIES=(\"Anime\" \"Kids Shows\" \"Stand-Up Comedy\" \"TV shows\")\n1073\t RADARR_EMBY_LIBRARIES=(\"Movies\" \"Kid's Movies\")\n1074\t\n1075\t# Sonarr shared settings\n1076\t SONARR_ORPHAN_AGE=7 # days — files must be older than this before eligible for deletion\n1077\t SONARR_MAX_DELETE_GB=10 # require --i-know-what-im-doing if deletion exceeds this\n1078\t SONARR_EXTENSIONS=(\"mkv\" \"mp4\" \"avi\" \"m4v\" \"ts\" \"wmv\" \"mov\")\n1079\t SONARR_PROTECTED_PATTERNS=(\n1080\t # Subtitles\n1081\t \"*.srt\" \"*.sub\" \"*.ass\" \"*.ssa\" \"*.idx\" \"*.vtt\"\n1082\t # Metadata\n1083\t \"*.nfo\" \"*.tbn\"\n1084\t # Images\n1085\t \"*.jpg\" \"*.jpeg\" \"*.png\" \"*.webp\" \"*.svg\"\n1086\t \"poster.*\" \"fanart.*\" \"backdrop.*\" \"clearlogo.*\"\n1087\t \"banner.*\" \"thumb.*\" \"landscape.*\"\n1088\t # Kodi/Emby extras — not tracked by Sonarr API\n1089\t \"*-trailer.*\" \"*-featurette.*\" \"*-behindthescenes.*\"\n1090\t \"*-interview.*\" \"*-scene.*\" \"*-short.*\" \"*-deleted.*\"\n1091\t \"*-clip.*\" \"*-other.*\"\n1092\t # Theme songs — stored in show folder, not tracked by arr\n1093\t \"theme.mp3\" \"theme.flac\" \"theme.wav\" \"theme.m4a\" \"theme.mka\"\n1094\t )\n1095\t\n1096\t# Radarr shared settings\n1097\t RADARR_ORPHAN_AGE=7 # days — files must be older than this before eligible for deletion\n1098\t RADARR_MAX_DELETE_GB=15 # require --i-know-what-im-doing if deletion exceeds this\n1099\t RADARR_EXTENSIONS=(\"mkv\" \"mp4\" \"avi\" \"m4v\" \"wmv\" \"mov\")\n1100\t RADARR_PROTECTED_PATTERNS=(\n1101\t # Subtitles\n1102\t \"*.srt\" \"*.sub\" \"*.ass\" \"*.ssa\" \"*.idx\" \"*.vtt\"\n1103\t # Metadata\n1104\t \"*.nfo\" \"*.tbn\"\n1105\t # Images\n1106\t \"*.jpg\" \"*.jpeg\" \"*.png\" \"*.webp\" \"*.svg\"\n1107\t \"poster.*\" \"fanart.*\" \"backdrop.*\" \"clearlogo.*\"\n1108\t \"banner.*\" \"thumb.*\" \"landscape.*\"\n1109\t # Kodi/Emby extras — not tracked by Radarr API\n1110\t \"*-trailer.*\" \"*-featurette.*\" \"*-behindthescenes.*\"\n1111\t \"*-interview.*\" \"*-scene.*\" \"*-short.*\" \"*-deleted.*\"\n1112\t \"*-clip.*\" \"*-other.*\"\n1113\t # Theme songs — stored in movie folder, not tracked by arr\n1114\t \"theme.mp3\" \"theme.flac\" \"theme.wav\" \"theme.m4a\" \"theme.mka\"\n1115\t )\n1116\t\n1117\t# Radarr/Sonarr TMDb/TVDB removed entry cleanup\n1118\t RADARR_DROPPED_ADD_EXCLUSION=true # add removed movies to import exclusion list\n1119\t SONARR_DROPPED_ADD_EXCLUSION=true # add removed series to import exclusion list\n1120\t\n1121\t# ━━━ Arr Failed/Stalled Recovery ━━━\n1122\t# Auto blocklist + re-search failed imports and stalled downloads.\n1123\t# Runs every 6 hours — schedule: 0 */6 * * *\n1124\t#\n1125\t# Targets four problem types:\n1126\t# importFailed — downloaded but arr couldn't import\n1127\t# importPending — downloaded, stuck waiting to import (won't self-resolve)\n1128\t# error status — serious failure not covered above\n1129\t# stalled — download stuck with no connections or progress\n1130\t#\n1131\t# Items newer than ARR_IMPORT_RECOVERY_AGE are skipped — gives arr time to retry first.\n1132\t# Per-host recovery toggles (HOST1_SONARR_RECOVERY etc.) live in host*.conf.\n1133\t ARR_IMPORT_RECOVERY_AGE=6 # hours — skip items newer than this\n1134\t # matches cron interval — items eligible after one missed cycle\n1135\t\n1136\t# ==============================================================================================\n1137\t# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────\n1138\t# ==============================================================================================\n1139\t# Session-based storage allocator using filesystem symlink indirection.\n1140\t# ffmpeg resolves the symlink ONCE at session start — existing sessions never affected.\n1141\t# Symlink flips between ramdisk and SSD are transparent to active streams.\n1142\t#\n1143\t# ⚠️ Docker mount — must use shared propagation so symlink flips work inside container:\n1144\t# --mount type=bind,source=/mnt/ram-transcode,target=/ext-ram-transcode,bind-propagation=shared\n1145\t# Standard path mappings use rprivate — symlink changes are NOT visible inside container.\n1146\t\n1147\t# ━━━ Transcode Manager ━━━\n1148\t# tmpfs mount point — created at array start by ramdisk_setup.sh.\n1149\t# Must exist before Emby starts so the symlink resolves correctly.\n1150\t RAMDISK_PATH=\"/mnt/ramdisk_transcodes\"\n1151\t\n1152\t# Ramdisk size and flip thresholds — defined per host in host*.conf.\n1153\t# All three are coupled — if size changes, thresholds must change with it.\n1154\t# HOST1_RAMDISK_SIZE / HOST2_RAMDISK_SIZE\n1155\t# HOST1_RAMDISK_WARN_GB / HOST2_RAMDISK_WARN_GB ← flip to SSD at this usage\n1156\t# HOST1_RAMDISK_LOW_GB / HOST2_RAMDISK_LOW_GB ← flip back to ramdisk at this usage\n1157\t\n1158\t# Symlink that Emby points at — this path NEVER changes regardless of ramdisk/SSD state.\n1159\t# Emby resolves the symlink once per session at start — symlink flips are transparent.\n1160\t# Must match the container path configured in Emby's Extra Parameters.\n1161\t TRANSCODE_LINK=\"/mnt/ram-transcode\"\n1162\t\n1163\t# SSD fallback location — defined per host in host*.conf (cache path differs per server):\n1164\t# HOST1_TRANSCODE_SSD / HOST2_TRANSCODE_SSD\n1165\t\n1166\t# Minimum free GB on SSD before allowing flip from ramdisk to SSD.\n1167\t RAMDISK_SSD_MIN_GB=20\n1168\t\n1169\t TRANSCODE_MAX_AGE=20 # minutes — HLS segment age before cleanup eligibility\n1170\t TRANSCODE_ORPHAN_AGE=30 # minutes — files with no matching active session\n1171\t TRANSCODE_FLIP_WARN=3 # notify if symlink flips this many times in one hour\n1172\t\n1173\t TRANSCODE_OWNER=\"nobody:users\"\n1174\t TRANSCODE_CHMOD=\"755\"\n1175\t\n1176\t# Operating mode — controls symlink direction behaviour.\n1177\t# smart — auto-flips between ramdisk and SSD based on thresholds (default)\n1178\t# ramdisk — always uses ramdisk, warns if RAMDISK_WARN_GB exceeded but holds\n1179\t# ssd — always uses SSD, never flips to ramdisk\n1180\t TRANSCODE_MANAGER_MODE=\"smart\"\n1181\t\n1182\t# Daily statistics log — read by weekly_health_digest.sh for transcode summary.\n1183\t TRANSCODE_STATE_FILE=\"$STATE_DIR/transcode_state.db\"\n1184\t TRANSCODE_DAILY_LOG=\"$DATA_DIR/transcode_daily.db\"\n1185\t TRANSCODE_LOG_RETENTION=90 # days before old entries purged\n1186\t\n1187\t TRANSCODE_CHECK_EMBY=true\n1188\t\n1189\t# ━━━ Transcode Server Array ━━━\n1190\t# All media servers sharing the ramdisk transcode space.\n1191\t# Format: \"ContainerName|URL|APIKey|Type\" — Type: emby | jellyfin | plex\n1192\t# Entries with placeholder API keys are skipped automatically.\n1193\t# ⚠️ Tdarr does NOT belong here — keep Tdarr on SSD, not ramdisk.\n1194\t# Defined per host in host*.conf — Emby container names and keys differ per server:\n1195\t# HOST1_TRANSCODE_SERVERS / HOST2_TRANSCODE_SERVERS\n1196\t\n1197\t# ==============================================================================================\n1198\t# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────\n1199\t# ==============================================================================================\n1200\t\n1201\t# ━━━ Certificate Monitor ━━━\n1202\t# Checks SSL certificate expiry via direct openssl connection — no NPM dependency.\n1203\t# Checks the actual certificate served by each domain, not what NPM thinks it has.\n1204\t# Domains defined per host in host*.conf — each server monitors its own domains:\n1205\t# HOST1_CERT_MONITOR_DOMAINS / HOST2_CERT_MONITOR_DOMAINS\n1206\t CERT_WARN_DAYS=30 # warn when cert expires within this many days\n1207\t CERT_CRIT_DAYS=7 # critical alert within this many days\n1208\t CERT_TIMEOUT=10 # seconds per domain before giving up\n1209\t\n1210\t# ━━━ Backup Verify ━━━\n1211\t# Verifies rsync mirror health by comparing random file checksums between servers.\n1212\t# Catches silent corruption or incomplete syncs that rsync itself wouldn't detect.\n1213\t# Defined per host in host*.conf — leave empty to use HOST*_DAILY_SYNC_SHARES automatically:\n1214\t# HOST1_BACKUP_VERIFY_SHARES / HOST2_BACKUP_VERIFY_SHARES\n1215\t BACKUP_VERIFY_SAMPLE=10 # random files to check per share\n1216\t BACKUP_VERIFY_MIN_SIZE=1M # minimum file size to include in sample\n1217\t\n1218\t# ━━━ SMART Health ━━━\n1219\t# Monitors drive SMART attributes — discovers all drives via /dev/sd* and /dev/nvme*.\n1220\t# Thresholds read from /boot/config/plugins/dynamix/dynamix.cfg at runtime\n1221\t# (hot/max/hotssd/maxssd) — these vars are fallback only if dynamix.cfg not found.\n1222\t SMART_TEMP_WARN=45 # fallback — Celsius warn threshold\n1223\t SMART_TEMP_CRIT=55 # fallback — Celsius critical threshold\n1224\t# Drives to ignore defined per host in host*.conf — hardware is server-specific:\n1225\t# HOST1_SMART_IGNORE_DRIVES / HOST2_SMART_IGNORE_DRIVES\n1226\t\n1227\t# ━━━ ZFS Memory Snapshot ━━━\n1228\t# Weekly ZFS pool health and memory diagnostic report — informational only.\n1229\t ZFS_REPORT_LOG=\"$DATA_DIR/zfs-weekly-health.log\"\n1230\t ZFS_REPORT_ARC_WARN_PCT=90 # warn if ARC using more than this % of its max\n1231\t ZFS_REPORT_FREE_WARN_GB=10 # warn if less than this GB free RAM\n1232\t ZFS_REPORT_AVAIL_WARN_GB=20 # warn if less than this GB available on ZFS pool\n1233\t ZFS_REPORT_DOCKER_TOP=10 # how many top Docker containers to show by memory\n1234\t# Pool ignore list defined per host in host*.conf — pool names are server-specific:\n1235\t# HOST1_ZFS_REPORT_IGNORE_POOLS / HOST2_ZFS_REPORT_IGNORE_POOLS\n1236\t\n1237\t# ━━━ Bandwidth Monitor ━━━\n1238\t# Called automatically by rsync.sh after each sync — one bounded write per run.\n1239\t# Tracks transfer size, duration and profile per sync for weekly summary reporting.\n1240\t BANDWIDTH_LOG=\"$DATA_DIR/bandwidth_history.db\"\n1241\t BANDWIDTH_LOG_RETENTION=90 # days before old entries purged\n1242\t BANDWIDTH_WARN_GB=50 # flag syncs larger than this in weekly report\n1243\t\n1244\t# Stats files — written by cleanup and recovery scripts, read by coffee report.\n1245\t# All in DATA_DIR — array always running when these are written.\n1246\t ARR_CLEANUP_STATS=\"$DATA_DIR/arr_cleanup_stats.db\" # lidarr/sonarr/radarr orphan stats\n1247\t ARR_RECOVERY_STATS=\"$DATA_DIR/arr_recovery_stats.db\" # blocklist + re-search stats\n1248\t\n1249\t# ━━━ Health Digest ━━━\n1250\t# Aggregated system health summary — reads existing state files, no new writes.\n1251\t# Three profiles control when the digest is sent:\n1252\t# always — sends every run regardless of findings\n1253\t# smart — sends only when DIGEST_SMART_ON_* conditions are found\n1254\t# weekly — sends once per week on DIGEST_DAY only\n1255\t DIGEST_PROFILE=\"weekly\" # always | smart | weekly\n1256\t DIGEST_DAY=\"Sunday\"\n1257\t DIGEST_SMART_ON_WATCHDOG=true # send if any watchdog strikes are active\n1258\t DIGEST_SMART_ON_FALLBACK=true # send if fallback state is not NORMAL\n1259\t DIGEST_SMART_ON_CERT_WARN=true # send if any cert is under CERT_WARN_DAYS\n1260\t DIGEST_SMART_ON_BANDWIDTH=true # send if any transfer exceeded BANDWIDTH_WARN_GB\n1261\t\n1262\t# ━━━ Emby Session Report ━━━\n1263\t# Weekly Emby usage statistics via API — no persistent writes, queries fresh each run.\n1264\t# URL and API key pulled from HOST*_EMBY_URL and HOST*_EMBY_API_KEY in host*.conf.\n1265\t EMBY_REPORT_DAYS=7 # days to include in the report period\n1266\t EMBY_REPORT_TOP_N=10 # number of top content items to show\n1267\t\n1268\t# ==============================================================================================\n1269\t# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────\n1270\t# ==============================================================================================\n1271\t# Pressure reduction layer — keeps the system comfortable before things break.\n1272\t# Called by watchdog_orchestrator.sh. Single-pass, not a continuous loop.\n1273\t#\n1274\t# ── PRESSURE LEVELS ───────────────────────────────────────────────────────────────────────────\n1275\t# Level 1 (soft) — throttle SABnzbd + qBit download speeds\n1276\t# Level 2 (medium) — further throttle + docker pause background containers\n1277\t# Level 3 (hard) — docker stop optional containers, signal docker_watchdog to defer\n1278\t#\n1279\t# ── PER-HOST CONTAINER LISTS ──────────────────────────────────────────────────────────────────\n1280\t# HOST*_RW_PAUSE_CONTAINERS — docker pause at medium pressure (in host*.conf)\n1281\t# HOST*_RW_STOP_CONTAINERS — docker stop at hard pressure (in host*.conf)\n1282\t\n1283\t RW_ENABLED=true\n1284\t RW_STATE_FILE=\"$STATE_DIR/resource_watchdog_state.db\"\n1285\t\n1286\t# ━━━ Pressure Thresholds ━━━\n1287\t# Graduated RAM response — resource_watchdog acts before system_watchdog reboots.\n1288\t# RW_RAM_SOFT_GB > RW_RAM_MEDIUM_GB > RW_RAM_HARD_GB > SYS_WATCHDOG_MEM_GB always\n1289\t RW_RAM_SOFT_GB=12 # throttle start — reduce background load\n1290\t RW_RAM_MEDIUM_GB=8 # pause background containers\n1291\t RW_RAM_HARD_GB=6 # stop optional containers (was SYS_WATCHDOG_MEM_SHUTDOWN_GB)\n1292\t RW_RAM_RECOVER_GB=20 # RAM must reach this before restoring hard-stopped containers\n1293\t\n1294\t# Load average thresholds — multiplier × core count\n1295\t RW_LOAD_SOFT_MULTIPLIER=2.0 # soft pressure: 2× cores sustained\n1296\t RW_LOAD_MEDIUM_MULTIPLIER=3.0 # medium pressure: 3× cores sustained\n1297\t\n1298\t# Consecutive runs at lower pressure before de-escalating\n1299\t RW_RECOVER_CYCLES=3\n1300\t\n1301\t# ━━━ SABnzbd Throttle ━━━\n1302\t# Speed values: \"50M\" = 50 MB/s, \"0\" = unlimited\n1303\t RW_SABNZBD_ENABLED=true\n1304\t RW_SABNZBD_SPEED_SOFT=\"50M\"\n1305\t RW_SABNZBD_SPEED_MEDIUM=\"10M\"\n1306\t\n1307\t# ━━━ qBittorrent Throttle ━━━\n1308\t# KB/s — 0 = unlimited\n1309\t RW_QBIT_ENABLED=true\n1310\t RW_QBIT_DL_SOFT=51200 # 50 MB/s\n1311\t RW_QBIT_DL_MEDIUM=10240 # 10 MB/s\n1312\t\n1313\t# ━━━ Critical Containers ━━━\n1314\t# Never paused or stopped regardless of pressure level.\n1315\t# Keep DNS, auth, media serving, and live TV always running.\n1316\t RW_CRITICAL_CONTAINERS=(\n1317\t \"NginxProxyManager\" # reverse proxy — internet access\n1318\t \"Authelia\" # auth — nothing accessible without it\n1319\t \"Authelia-Secondary\"\n1320\t \"Mariadb-Authelia\" # Authelia dependency\n1321\t \"Mariadb-Authelia-Secondary\"\n1322\t \"Redis-Authelia\" # Authelia dependency\n1323\t \"Redis-Authelia-Secondary\"\n1324\t \"AdGuard-Home\" # DNS — all LAN resolution\n1325\t \"Emby\" # media server — Live TV buffering\n1326\t \"Dispatcharr\" # Live TV scheduler — loses state if stopped\n1327\t \"Dispatcharr-Basic\"\n1328\t \"Dispatcharr-Iptv-Users\"\n1329\t )\n1330\t\n1331\t# ==============================================================================================\n1332\t# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────\n1333\t# ==============================================================================================\n1334\t# Single-pass system health check — last line of defense before a crash.\n1335\t# Called by watchdog_orchestrator.sh every minute. NOT started by array_started.sh.\n1336\t# Re-sources config at each orchestrator run — config changes take effect immediately.\n1337\t#\n1338\t# ── THREE-TIER RESPONSE SYSTEM ────────────────────────────────────────────────────────────────\n1339\t# CRITICAL — bypass ALL strikes, reboot immediately\n1340\t# Docker daemon down, rootfs 100%, kernel oops, FD exhaustion, /boot read-only\n1341\t#\n1342\t# URGENT — bypass strikes only when OOM confirms active crisis\n1343\t# RAM < MEM_GB AND OOM kills >= OOM_LIMIT in this cycle → reboot NOW\n1344\t# Without OOM confirmation → normal strike system\n1345\t#\n1346\t# STANDARD — strike system (N consecutive failures → reboot)\n1347\t# RAM tiers, high load, CPU temp, zombies, /var/log, /tmp, containers\n1348\t#\n1349\t# ── RAM ───────────────────────────────────────────────────────────────────────────────────────\n1350\t# SYS_WATCHDOG_MEM_GB — strike system → reboot (or OOM bypass)\n1351\t# Warn/shutdown/recover RAM tiers are handled by resource_watchdog.sh\n1352\t\n1353\t# ━━━ State Files ━━━\n1354\t SYS_WATCHDOG_STATE_FILE=\"$STATE_DIR/system_watchdog_state.db\"\n1355\t DOCKER_WATCHDOG_FAILED_FILE=\"$STATE_DIR/docker_watchdog_failed.db\"\n1356\t SYS_WATCHDOG_REBOOT_LOG=\"$STATE_DIR/system_watchdog_reboots.db\"\n1357\t SYS_WATCHDOG_OOM_FILE=\"$STATE_DIR/system_watchdog_oom.db\"\n1358\t\n1359\t# ━━━ Strike and Reboot Loop Settings ━━━\n1360\t# Strike system: a check must fail this many consecutive cycles before action is taken.\n1361\t# Single spikes (one bad reading) are ignored — sustained problems trigger reboot.\n1362\t SYS_WATCHDOG_STRIKE_LIMIT=2 # consecutive failures before reboot trigger\n1363\t\n1364\t# Reboot loop protection — if system keeps rebooting something is seriously wrong.\n1365\t# After REBOOT_LIMIT reboots in REBOOT_WINDOW_HRS → shutdown instead of reboot.\n1366\t SYS_WATCHDOG_REBOOT_LIMIT=3\n1367\t SYS_WATCHDOG_REBOOT_WINDOW_HRS=12\n1368\t\n1369\t# ━━━ RAM Reboot Threshold ━━━\n1370\t# Reboot trigger only — warn/shutdown/recover handled by resource_watchdog.sh\n1371\t# RW_RAM_HARD_GB > SYS_WATCHDOG_MEM_GB always (RM acts before watchdog reboots)\n1372\t SYS_WATCHDOG_MEM_GB=4 # strike system → reboot\n1373\t\n1374\t# ━━━ OOM Bypass Settings ━━━\n1375\t# OOM bypass: if RAM is critically low AND kernel OOM kills exceed this threshold\n1376\t# in a single cycle → bypass strike system and reboot immediately.\n1377\t# Rate-based: kills per 5-minute cycle, not absolute count.\n1378\t# Rationale: 1-2 kills = docker_watchdog handles it ✅\n1379\t# 3+ kills while RAM critical = system dying faster than watchdogs can heal ✅\n1380\t SYS_WATCHDOG_OOM_LIMIT=3 # OOM kills in one cycle to trigger bypass\n1381\t\n1382\t# ━━━ Thresholds ━━━\n1383\t# Set at \"about to become unstable\" — not \"things are a bit high\".\n1384\t\n1385\t# rootfs (/) usage — two levels: strike at 95%, critical bypass at 99%\n1386\t SYS_WATCHDOG_ROOTFS_PCT=95 # standard strike threshold\n1387\t SYS_WATCHDOG_ROOTFS_CRITICAL_PCT=99 # bypass strikes — truly full, writes failing\n1388\t\n1389\t# /var/log usage — log spam indicates something broken\n1390\t SYS_WATCHDOG_LOG_PCT=95\n1391\t\n1392\t# /tmp usage — tmpfs fills from downloads, lock files fail at 100%\n1393\t SYS_WATCHDOG_TMP_PCT=90 # warn + attempt clear\n1394\t SYS_WATCHDOG_TMP_CRITICAL_PCT=98 # bypass strikes if clear failed\n1395\t\n1396\t# ZFS ARC pinned percentage — ARC not releasing after reclaim = memory stuck\n1397\t SYS_WATCHDOG_ARC_PINNED_PCT=98\n1398\t SYS_WATCHDOG_ARC_RELEASE_PCT=95\n1399\t\n1400\t# Load average multiplier — threshold = MULTIPLIER × CPU core count\n1401\t# MULTIPLIER=3 on 16-core = load of 48 before triggering\n1402\t SYS_WATCHDOG_LOAD_MULTIPLIER=3\n1403\t\n1404\t# Zombie process count — large numbers = serious process management failure\n1405\t SYS_WATCHDOG_ZOMBIE_LIMIT=50\n1406\t\n1407\t# CPU temperature — sustained high temp causes throttling or kernel panic\n1408\t SYS_WATCHDOG_CPU_TEMP_MAX=95\n1409\t\n1410\t# File descriptor exhaustion — system-wide FD limit near exhaustion\n1411\t# New connections fail silently, Docker can't spawn processes, SSH fails\n1412\t SYS_WATCHDOG_FD_CRITICAL_PCT=95 # bypass strikes — critical tier\n1413\t\n1414\t# Runaway process — single non-container process consuming excessive CPU\n1415\t# Multiple strikes before action — single spikes are normal\n1416\t SYS_WATCHDOG_RUNAWAY_CPU_PCT=90 # % single process must sustain\n1417\t SYS_WATCHDOG_RUNAWAY_STRIKES=3 # consecutive cycles before warning\n1418\t\n1419\t# Array disk errors — accumulating mdstat errors = disk failing NOW\n1420\t SYS_WATCHDOG_MDSTAT_ERROR_LIMIT=5 # new errors in one cycle before acting\n1421\t\n1422\t# ━━━ Check Toggles ━━━\n1423\t# Per-host — moved to host*.conf\n1424\t# Different servers may have different hardware, NICs, and check requirements\n1425\t# See HOST*_SYS_WATCHDOG_CHECK_* in host*.conf\n1426\t\n1427\t# ━━━ Abort Toggles ━━━\n1428\t# Conditions that prevent reboot even when a threshold is hit.\n1429\t# CRITICAL tier bypasses these — a truly critical condition reboots regardless.\n1430\t# true = abort standard reboot if this condition is active\n1431\t# false = reboot anyway\n1432\t SYS_WATCHDOG_ABORT_ON_ZFS_UNHEALTHY=true # rebooting with bad pool risks data loss\n1433\t SYS_WATCHDOG_ABORT_ON_PARITY=false # aborting parity better than crashing mid-check\n1434\t SYS_WATCHDOG_ABORT_ON_MOVER=false # aborting move better than crashing mid-move\n1435\t\n1436\t# ==============================================================================================\n1437\t# ──────────────────────── End Of User Variables ───────────────────────────────────────────────\n1438\t# ==============================================================================================\n1439\t"}]},"uuid":"27b1ca71-24ca-4b96-991d-a2dbaa5fc50e","timestamp":"2026-06-06T17:53:25.596Z","toolUseResult":{"type":"text","file":{"filePath":"/boot/config/plugins/varaverk/Configurations/master.conf","content":"#!/bin/bash\n# ==============================================================================================\n# ================================= MASTER CONFIGURATION =======================================\n# ==============================================================================================\n# Shared configuration for the unRAID script ecosystem.\n# Contains all settings that apply to every server — thresholds, toggles, profiles, job lists.\n#\n# ── HOW THE THREE-FILE SYSTEM WORKS ──────────────────────────────────────────────────────────\n# Scripts source all three files at startup:\n# source master.conf ← shared config (this file)\n# source host1.conf ← HOST1 credentials, shares, container lists\n# source host2.conf ← HOST2 credentials, shares, container lists\n#\n# Sparse checkout (git) ensures each server only pulls its own host*.conf.\n# HOST2 never sees HOST1 credentials. HOST1 never sees HOST2 credentials.\n#\n# What belongs here: thresholds, toggles, intervals, profiles, job lists\n# What belongs in HOST*: hostnames, SSH keys, API keys, passwords, share paths,\n# container names, failover lists, watchdog containers\n#\n# ── HOW THIS FILE WORKS ───────────────────────────────────────────────────────────────────────\n# Every script sources all three conf files and common.sh at startup.\n# Change a value here and it affects all scripts that use it — no hunting through files.\n# To disable something: comment it out with # rather than deleting it.\n# To add a new rsync profile: add a key to each PROFILE_* array in the RSYNC section.\n# To add or remove orchestrator jobs: edit the arrays in the ORCHESTRATORS section.\n#\n# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────\n#\n# Section Description\n# ───────────────────────────────────────────────────────────────────────────────────────────\n# SHARED HOST CONFIGURATION DATA_DIR and shared connection settings\n# PARTNERSHIP Mirror relationship lifecycle — onboard/offboard/transfer\n# LOGGING Enable or disable verbose logging\n# NOTIFICATIONS unRAID native and Discord webhook settings\n# GIT / REPO Gitea repository and SSH settings\n#\n# ── ORCHESTRATORS ──────────────────────────────────────────────────────────────────────────\n# ARRAY START Scripts launched at array start (array_started.sh)\n# ARRAY STOP Scripts run at planned shutdown (array_stopping.sh)\n# WATCHDOG ORCHESTRATOR Per-minute watchdog runner (watchdog_orchestrator.sh)\n# SYSTEM WATCHDOG Sub-scripts called by watchdog_orchestrator.sh\n# CRITICAL SYNC MAINTENANCE 30-minute jobs + sync shares + partnership check (critical_sync_maintenance.sh)\n# INTERMEDIATE SYNC MAINTENANCE Arr sync + optional mid-day rsync (intermediate_sync_maintenance.sh)\n# DAILY SYNC MAINTENANCE Job list + media shares (daily_sync_maintenance.sh)\n# WEEKLY SYNC MAINTENANCE Job list + sync shares + update toggles (weekly_sync_maintenance.sh)\n# MONTHLY MAINTENANCE Uptime-triggered heavy tasks — ZFS scrub, SMART tests (monthly_maintenance.sh)\n#\n# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────\n# RSYNC ENABLE/DISABLE Two-tier toggle system — global gate + per-orchestrator\n# RSYNC DEFAULTS Global fallback rsync options and limits\n# REMOTE HEALTH CHECKS Rootfs threshold for pre-flight abort\n# RSYNC PROFILE SYSTEM Per-profile overrides for appdata syncs\n#\n# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────\n# FALLBACK Mutual container failover shared settings\n# FALLBACK TEST Simulated outage settings for fallback_test.sh\n#\n# ── DOCKER ESSENTIALS ──────────────────────────────────────────────────────────────────────\n# DOWNLOADERS RESET Retention and thresholds for slskd, SABnzbd, qBittorrent\n# DOCKER DAILY RESTART Containers restarted daily\n# DOCKER WEEKLY RESTART Containers restarted weekly\n# DOCKER WATCHDOG Thresholds and toggles for container monitoring\n# DOCKER NETWORK CONNECT Networks to ensure + containers to connect\n#\n# ── UNRAID ESSENTIALS ──────────────────────────────────────────────────────────────────────\n# INOTIFY TUNING inotify limits — raised at array start by inotify_tuning.sh\n# SYSTEM TUNING MONITOR Tracks inotify + php-fpm usage over time\n# REBOOT User warning delay before scheduled reboot\n# MOVER Mover stop timeout\n# SYSLOG FILTER Docker veth noise filter file path\n# PHP-FPM PHP-FPM max children config\n# CLEAR LOGS System log file paths\n# WEBGUI WATCHDOG WebGUI nginx + emhttp monitoring and restart\n#\n# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────\n# MEDIA PERMISSIONS Share list, mode and owner for permissions script\n# MEDIA CLEANER Anime and media folder lists and file patterns\n# ARR CLEANUP Lidarr, Sonarr, Radarr orphan file cleanup settings\n# ARR FAILED/STALLED RECOVERY Auto blocklist + re-search settings\n#\n# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────\n# TRANSCODE MANAGER Ramdisk and SSD fallback transcode management\n# TRANSCODE SERVER ARRAY Multi-server session monitoring (Emby, Jellyfin, Plex)\n#\n# ── MONITORS ───────────────────────────────────────────────────────────────────────────────\n# CERTIFICATE MONITOR SSL certificate expiry monitoring\n# BACKUP VERIFY Random sample checksum verification against remote\n# SMART HEALTH Drive SMART attribute monitoring\n# ZFS MEMORY SNAPSHOT Weekly ZFS health and memory diagnostic report\n# BANDWIDTH MONITOR Daily rsync transfer logging and weekly summary\n# HEALTH DIGEST Aggregated system health digest — always/smart/weekly\n# EMBY SESSION REPORT Weekly Emby usage statistics via API\n#\n# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────\n# SYSTEM WATCHDOG Continuous system health monitoring — last line of defense\n#\n# ==============================================================================================\n\n# ==============================================================================================\n# ── HOST IDENTITIES ───────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Hostnames for every server in the ecosystem — not credentials, safe for all machines.\n# Must match the exact unRAID hostname AND Tailscale device name (case sensitive).\n# detect_hosts() in common.sh matches the local hostname against these to set MY_ID / REMOTE_ID.\n# Tailscale IP resolution uses these names — no hardcoded IPs needed.\n# To add a new server: add HOST3=\"unRAID-NewServer\" here + create host3.conf.\n HOST1=\"unRAID-Gmer4Lfe\"\n HOST2=\"unRAID-Jayred365\"\n\n# ==============================================================================================\n# ── SHARED HOST CONFIGURATION ─────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Requirement: internal NVMe/SSD boot drive. Everything lives in the plugin folder on flash —\n# scripts, state, and data are all available before the array mounts.\n# Both directories are created automatically if they don't exist.\n#\n# DATA_DIR — historical logs, statistics, discovery histories, blocklists\n# STATE_DIR — runtime state files for all scripts (watchdogs, fallback, transcode, etc.)\n# Requirement: ALL state files MUST use $STATE_DIR. No /tmp, no /boot/config root.\n DATA_DIR=\"/boot/config/plugins/varaverk/data\"\n STATE_DIR=\"/boot/config/plugins/varaverk/State_Files\"\n\n# ── Version Parity ──\n# Controls behaviour when local and remote unRAID versions differ.\n# Major version mismatch always aborts regardless of this setting.\n# \"warn\" — log warning and continue (safe for minor/patch differences)\n# \"abort\" — refuse to continue (strict — ensures both sides always match)\n UNRAID_VERSION_MISMATCH_ACTION=\"warn\"\n\n# ── Arr Sync ──\n# arr_sync.sh syncs Lidarr/Sonarr/Radarr libraries across all nodes bidirectionally.\n# Runs before rsync — all nodes agree on tracked library before files are transferred.\n# Remote API keys are read live from each node's config.xml via SSH — never stored here.\n ARR_SYNC_ENABLED=true\n ARR_SYNC_BLOCKLIST=\"${DATA_DIR}/arr_sync_blocklist.tsv\"\n ARR_SYNC_CONNECT_TIMEOUT=10 # seconds — SSH connect timeout per node\n ARR_SYNC_API_TIMEOUT=60 # seconds — curl timeout for library fetches\n DOCKER_APPDATA_BASE=\"/mnt/user/appdata\"\n ARR_SYNC_LIDARR_PORT=8686\n ARR_SYNC_SONARR_PORT=8989\n ARR_SYNC_RADARR_PORT=7878\n\n# ── Remote Docker Daemon ──\n# Strike system for remote Docker daemon health checks.\n# Scripts that issue remote container commands check the remote daemon first.\n# Below limit → skip operation this run | At limit → notify critical + exit\n REMOTE_DOCKER_STRIKE_LIMIT=3 # consecutive failures before critical notify\n\n# ==============================================================================================\n# ── PARTNERSHIP ───────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Manages the relationship lifecycle between two unRAID servers.\n# HOST1 is always the owner (source of truth) — HOST2 is always the mirror.\n# PARTNERSHIP_OWNER_HOST flips to \"HOST2\" after a --transfer operation.\n# All identity vars (hostnames, SSH keys) live in host*.conf.\n# Hostnames already match Tailscale device names — IP resolution is automatic.\n#\n# State files in STATE_DIR — survives reboots, array must be up (scripts run after array start):\n# STATE_DIR/partnership_HOST1.db ← HOST1 writes only\n# STATE_DIR/partnership_HOST2.db ← HOST2 writes only\n# STATE_DIR/partnership_blocklist.db ← shared blocklist\n# STATE_DIR/varaverk_setup.db ← onboarding progress state\n# Propagated via SSH — no rsync needed\n#\n# critical_sync_maintenance.sh runs --check every 30min:\n# Reads both state files via SSH\n# Detects offboard requests → finalises from owner side\n# Increments offline counter → auto-offboards after threshold\n# Silent when healthy ✅\n#\n# See README-Partnership.md for full lifecycle documentation.\n\n PARTNERSHIP_ENABLED=false\n PARTNERSHIP_OWNER_HOST=\"HOST1\" # \"HOST1\" or \"HOST2\" — flips on --transfer\n\n# Auth containers reconfigured on onboard/offboard — defined per host in host*.conf.\n# Format: \"ContainerName|WebUIPort\"\n# HOST1_PARTNERSHIP_AUTH_WEBUIS / HOST2_PARTNERSHIP_AUTH_WEBUIS\n# On onboard → WebUI pointed at owner's Tailscale IP\n# On offboard → WebUI pointed back at localhost\n\n# Paths to collect during the grace window after offboard — defined per host in host*.conf.\n# HOST1_PARTNERSHIP_MIRROR_BACKUPS / HOST2_PARTNERSHIP_MIRROR_BACKUPS\n# Notified on offboard — no auto-deletion, manual collection.\n\n# Timing — single var controls both Tailscale removal and backup access expiry.\n# Both expire at the same time — keeping backups accessible beyond Tailscale removal is pointless.\n PARTNERSHIP_GRACE_HOURS=6 # hours after offboard before Tailscale removal\n # backup access expires at the same time\n PARTNERSHIP_OFFLINE_THRESHOLD=30 # days either server unreachable before auto-offboard\n # works both directions independently\n\n# Partnership and setup state files — all in STATE_DIR per the project requirement.\n# Scripts use these variables; do not hardcode /boot/config paths in scripts.\n PARTNERSHIP_BLOCKLIST_FILE=\"$STATE_DIR/partnership_blocklist.db\"\n VARAVERK_SETUP_FILE=\"$STATE_DIR/varaverk_setup.db\"\n\n# Tailscale removal on offboard.\n PARTNERSHIP_REMOVE_TAILSCALE=true # remove mirror from Tailscale tailnet on offboard\n # false = skip removal (manual or testing)\n\n# Tailscale API — required when PARTNERSHIP_REMOVE_TAILSCALE=true.\n# Stays in shared conf — only owner uses it, and owner is always running this script.\n# API key: https://login.tailscale.com/admin/settings/keys → Devices write scope\n TAILSCALE_API_KEY=\"\" # tskey-api-...\n TAILSCALE_TAILNET=\"\" # your tailnet name (e.g. yourname.github)\n\n# Transfer safety.\n PARTNERSHIP_TRANSFER_CONFIRM=\"i-understand-this-transfers-ownership\"\n PARTNERSHIP_TRANSFER_STRIKES=3 # consecutive health checks required\n PARTNERSHIP_TRANSFER_MAX_ATTEMPTS=20 # max health check attempts before giving up\n\n# Onboard settings.\n PARTNERSHIP_ONBOARD_VERIFY=true # verify WebUI reachable after reconfiguration\n PARTNERSHIP_ONBOARD_NOTIFY=true # notify both servers on completion\n PARTNERSHIP_SYNC_INTERVAL=15 # minutes — informational, actual schedule in cron\n\n# SSH key lifecycle — managed by Partnership/ssh_setup.sh.\n# Key named after this server: hostname lowercased, unraid- prefix stripped.\n# unRAID-Gmer4Lfe → /root/.ssh/gmer4lfe_rsync_automation\n# Run Partnership/partnership_onboard.sh to generate, copy, and update conf automatically.\n SSH_MAX_STRIKES=5 # consecutive SSH auth failures before critical notify\n SSH_STRIKE_RESET_HRS=24 # hours since last failure before strike counter resets\n\n# FolderView3 plugin integration — chodeus/VladoPortos/scolcipitato.\n# Creates a \"{Partner}-Failover\" folder on onboard, removes + cleans containers on offboard.\n# Folder name derived from remote hostname (strip unraid- prefix case-insensitively).\n# Plugin config: /boot/config/plugins/folder.view3/docker.json\n PARTNERSHIP_FOLDERVIEW3=true # create/remove FolderView3 folder on onboard/offboard\n PARTNERSHIP_FOLDERVIEW3_URL=\"\" # CA plugin URL — leave empty to skip auto-install\n\n# ==============================================================================================\n# ── LOGGING ───────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# Controls verbose [LOG] output across all scripts.\n# true = show detailed [LOG] lines — useful for debugging or first-time setup\n# false = show only user-facing output — cleaner for scheduled runs\n ENABLE_LOGGING=false\n\n# ==============================================================================================\n# ── NOTIFICATIONS ─────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# unRAID native notification system — integrates with the bell icon in the WebGUI.\n# normal = job completed successfully / warning = something failed or needs attention\n NOTIFY_UNRAID=true\n\n# Discord webhook URL — defined per host in host*.conf.\n# HOST1_DISCORD_WEBHOOK / HOST2_DISCORD_WEBHOOK\n# Allows different webhooks per server, or only one server notifying.\n\n# ==============================================================================================\n# ── GIT / REPO ────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# Gitea self-hosted repository — used by git_pull_execute.sh.\n# Detects Gitea container location at runtime — works through failover automatically.\n# Falls back to GITEA_DOMAIN if local and Tailscale both fail.\n GITEA_CONTAINER=\"Gitea\"\n GITEA_REPO_PATH=\"FailedProxy/Varaverk.git\"\n GITEA_DOMAIN=\"\" # e.g. git.yourdomain.com — requires NPM + DNS\n TARGET_DIR=\"/boot/config/plugins/varaverk\"\n GITEA_SSH_KEY=\"/root/.ssh/unraid_gitea\"\n SSH_PORT=221 # Gitea SSH port (default 22, Gitea often uses 221/222)\n GITEA_HTTP_PORT=3000 # Gitea web/API port — used by gitea_ssh_setup.sh\n\n# ==============================================================================================\n# ── ORCHESTRATORS ─────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# All orchestrator job lists live here — edit arrays to add/remove scripts.\n# No changes to orchestrator scripts needed when adding or removing jobs.\n# Sections ordered by run frequency: array events first, then shortest interval to longest.\n\n# ━━━ Array Start ━━━\n# Scripts launched by array_started.sh when the array comes online.\n# Launched in order — each as a background process.\n# One-shot scripts (ramdisk, syslog, fpm, inotify, network) run and exit naturally.\n# Continuous scripts (failover) run until array stops.\n# Watchdogs (resource_watchdog, docker_watchdog, system_watchdog) are cronned via\n# watchdog_orchestrator.sh — NOT launched here.\n ARRAY_START_SCRIPTS=(\n \"Plugin/unraid/System_Essentials/unraid_api_key_renew.sh\" # re-register Varaverk API key — registry is ephemeral\n \"System_Essentials/conf_sync.sh\" # pull partner confs + push own conf into /tmp/.vv/ RAM cache\n \"Transcodes/ramdisk_setup.sh\" # creates ramdisk + symlink before Emby starts\n \"System_Essentials/docker_syslog_filter.sh\" # suppress veth noise before logs fill\n \"Plugin/unraid/System_Essentials/php_fpm_max_children.sh\" # WebGUI performance tuning\n \"System_Essentials/inotify_tuning.sh\" # bump inotify limits — containers miss events if exhausted\n \"Docker_Essentials/docker_network_connect.sh\" # ensure networks exist + connect containers\n \"Tools/claude_startup.sh\" # persist Claude data + binary to appdata; re-symlink on boot\n \"Fallback/fallback.sh\" # mutual failover — continuous\n )\n\n# ━━━ Array Stop ━━━\n# Scripts run by array_stopping.sh for a planned shutdown — stops everything cleanly in order.\n# Run sequentially (foreground) — each must complete before the next starts.\n# Order matters: user scripts first (prevents new ops), then data movement, then containers.\n ARRAY_STOP_SCRIPTS=(\n \"Plugin/unraid/System_Essentials/user_scripts_stop.sh\" # stop background scripts before they start new ops\n \"Fallback/fallback.sh --stop\" # gracefully stop fallback (not caught by user_scripts_stop)\n \"System_Essentials/rsync_stop.sh --rsync-only\" # kill rsync; skip container recovery (handled below)\n \"Plugin/unraid/System_Essentials/mover_stop.sh\" # stop mover after rsync (they conflict on same files)\n \"Docker_Essentials/docker_container_stop.sh\" # stop all containers last\n )\n\n# ━━━ Watchdog Orchestrator ━━━\n# watchdog_orchestrator.sh runs WATCHDOG_ORCHESTRATOR_SCRIPTS in order each cron cycle.\n# Schedule: * * * * * (every minute)\n# NOT in ARRAY_START_SCRIPTS — has its own cron entry.\n# Order matters — resource first (frees pressure), docker second (heals with freed resources),\n# system third (storage + webgui component health), stability last (last line of defense).\n WATCHDOG_ORCHESTRATOR_SCRIPTS=(\n \"Watchdogs/resource_watchdog.sh\" # reduce system pressure before healing attempts\n \"Watchdogs/docker_watchdog.sh\" # heal containers with freed resources\n \"Watchdogs/system_watchdog.sh\" # system component health — storage + webgui\n \"Plugin/unraid/System_Essentials/unraid_api_key_renew.sh\" # re-register API key if registry lost it\n \"Watchdogs/stability_watchdog.sh\" # reboot if all else fails — last line of defense\n )\n\n# ━━━ System Watchdog ━━━\n# system_watchdog.sh runs SYSTEM_WATCHDOG_SCRIPTS sequentially each cycle.\n# Called by watchdog_orchestrator.sh — not scheduled directly.\n SYSTEM_WATCHDOG_SCRIPTS=(\n \"Watchdogs/System/storage_watchdog.sh\" # pool growth + runaway log detection\n \"Plugin/unraid/Watchdogs/System/webgui_watchdog.sh\" # WebGUI availability — nginx → php-fpm → emhttp\n \"Watchdogs/System/network_watchdog.sh\" # internet, DDNS, Tailscale, NPM proxy\n )\n\n# ━━━ Critical Sync Maintenance ━━━\n# critical_sync_maintenance.sh runs every 30 minutes.\n# Order: CRITICAL_MAINTENANCE_SCRIPTS (jobs) → CRITICAL_SYNC_SHARES (rsync) → partnership --check\n# partnership --check always runs last regardless of rsync gate.\n# Comment out entries to disable without removing.\n CRITICAL_MAINTENANCE_SCRIPTS=(\n \"Docker_Essentials/downloaders_reset.sh\" # clear stuck download states every 30min\n \"Media/play_state_sync.sh\" # sync watched/resume state across Emby + Jellyfin\n )\n\n# Shares synced every 30 minutes — defined per host in host*.conf.\n# HOST1_CRITICAL_SYNC_SHARES / HOST2_CRITICAL_SYNC_SHARES\n# Format: \"/path/to/share\" or \"/path/to/share|profile-name\"\n# Order matters — Critical-Data first (auth stack), then Emby dirty sync.\n\n# ━━━ Intermediate Sync Maintenance ━━━\n# intermediate_sync_maintenance.sh runs every 4 hours — arr library sync, artwork fetch,\n# and optional mid-day rsync for any shares that need sub-daily propagation.\n# Schedule: 0 */4 * * *\n # INTERMEDIATE_SYNC_SHARES is host-specific — configure HOST*_INTERMEDIATE_SYNC_SHARES in host*.conf.\n\n INTERMEDIATE_MAINTENANCE_SCRIPTS=(\n \"Media/arrs_failed_stalled_recovery.sh\" # blocklist + re-search failed/stalled arr queue items\n )\n # arr_sync.sh runs as a fixed first step in intermediate_sync_maintenance.sh — not listed here.\n # It is controlled by ARR_SYNC_ENABLED (see Arr Sync section above).\n\n# ━━━ Daily Sync Maintenance ━━━\n# daily_sync_maintenance.sh runs media share sync first, then iterates\n# DAILY_MAINTENANCE_SCRIPTS for all jobs.\n# Schedule: 0 1 * * * (1am daily)\n DAILY_MAINTENANCE_SCRIPTS=(\n \"git_pull_execute.sh\" # pull latest scripts — always runs first\n \"Media/media_shares_permissions.sh\" # apply permissions — runs before cleaners\n \"Media/media_cleaner.sh anime\" # remove junk from anime shares\n \"Media/media_cleaner.sh media\" # remove junk from media shares\n #\"Media/lidarr_cleanup.sh\" # remove orphaned music files — enable when ready\n #\"Media/sonarr_cleanup.sh\" # remove orphaned TV files — enable when ready\n \"Media/radarr_cleanup.sh\" # remove orphaned movie files\n \"Media/lidarr_missing_art.sh\" # fetch missing album/artist artwork (HOST1 only — self-guards)\n \"Media/radarr_tmdb_removed.sh\" # remove movies dropped from TMDb\n \"Media/sonarr_tvdb_removed.sh\" # remove series dropped from TVDB\n \"Docker_Essentials/docker_update.sh\" # pull container image updates before restart\n \"Docker_Essentials/docker_daily_restart.sh\" # daily container restarts — runs last\n )\n\n# Pull latest images for DAILY_RESTART_CONTAINERS before the daily restart.\n# Containers keep running during pull — no extra downtime.\n# Set false to skip updates while still running the daily restart.\n DAILY_CONTAINER_UPDATES=true\n\n# Media shares synced daily by daily_sync_maintenance.sh.\n# Defined per-host in host*.conf — HOST1_DAILY_SYNC_SHARES and HOST2_DAILY_SYNC_SHARES.\n# Mesh model: every node pushes every media share. rsync has no --delete so pushes are additive.\n# arr_sync (union) ensures all arr libraries converge first. arr_cleanup removes true orphans.\n# Any node can download content to any share — it propagates to all nodes on the next cycle.\n# Adding HOST3: list every media share in HOST3_DAILY_SYNC_SHARES. No ownership to track.\n# These shares use DEFAULT_RSYNC_OPTS — no profile entry needed.\n# For shares needing custom options or container stops — create a profile in RSYNC section.\n\n# Personal encrypted shares defined per-host in host*.conf.\n# ZFS encrypted at dataset level — remote receives encrypted blocks, cannot read content.\n# See README-Rsync_Setup.md for ZFS encryption setup before uncommenting.\n\n# ━━━ Weekly Sync Maintenance ━━━\n# weekly_sync_maintenance.sh stops containers both sides → pulls updates →\n# syncs WEEKLY_SYNC_SHARES → restarts → then iterates WEEKLY_MAINTENANCE_SCRIPTS.\n# Schedule: 30 2 * * 0 (Sunday 2:30am)\n WEEKLY_MAINTENANCE_SCRIPTS=(\n \"Docker_Essentials/docker_weekly_restart.sh\" # weekly container restarts after sync\n \"Docker_Essentials/docker_update_remaining.sh\" # pull updates for all other containers\n \"System_Essentials/clear_logs.sh\" # purge aged logs — Sunday only, low priority\n \"Media/playback_aware_lidarr_discovery.sh\" # behavior-driven music discovery using weekly Emby playback history\n \"Media/playback_aware_radarr_discovery.sh\" # behavior-driven movie discovery using TMDB recommendations\n \"Media/playback_aware_sonarr_discovery.sh\" # behavior-driven TV discovery using TMDB recommendations\n )\n\n# Pull updates for all running containers NOT in daily/weekly restart lists.\n# Ensures every deployed container receives at least one image pull per week.\n# Set false to skip — docker_weekly_restart.sh still runs regardless.\n WEEKLY_REMAINING_UPDATES=true\n\n# Shares synced during the weekly maintenance window — defined per host in host*.conf.\n# HOST1_WEEKLY_SYNC_SHARES / HOST2_WEEKLY_SYNC_SHARES\n# Containers stopped both sides before sync — full clean state guaranteed.\n# Profiles drive container stops, excludes, and options — configure in RSYNC section.\n# Order matters — Emby first (larger), then Critical-Data (auth stack).\n\n# Container update toggles for the weekly sync window.\n# Containers already stopped for sync — updates pull at no extra downtime.\n# Both false → sync only, no updates.\n WEEKLY_SYNC_UPDATES=true # pull container updates locally during weekly window\n WEEKLY_SYNC_UPDATES_REMOTE=true # pull container updates on remote via SSH\n\n# ━━━ Monthly Maintenance ━━━\n# monthly_maintenance.sh fires only when BOTH gates pass:\n# 1. Server uptime >= MONTHLY_UPTIME_THRESHOLD_DAYS days\n# 2. Last run was >= MONTHLY_RUN_INTERVAL_DAYS days ago (or never run)\n# Cron: 0 3 * * * (daily 3am check — script self-gates, calling daily is safe)\n# NOT in WATCHDOG_ORCHESTRATOR_SCRIPTS — has its own cron entry.\n# Add scripts that require a long-stable settled system — scrubs, extended drive tests.\n# State file on /boot/config — survives reboots (interval gate independent of uptime gate).\n MONTHLY_MAINTENANCE_SCRIPTS=(\n \"Tools/zfs_pool_scrub.sh\" # ZFS pool integrity scrub — all pools, skip ZFS_REPORT_IGNORE_POOLS\n \"Tools/smart_long_test.sh\" # SMART extended self-test — all drives, sequential\n )\n\n MONTHLY_UPTIME_THRESHOLD_DAYS=30 # minimum uptime in days before maintenance fires\n MONTHLY_RUN_INTERVAL_DAYS=30 # minimum days since last run before running again\n MONTHLY_LAST_RUN_FILE=\"$STATE_DIR/monthly_maintenance_last_run.db\"\n\n# ━━━ Sunday Morning Coffee Report ━━━\n# Orchestrator that runs all Sunday monitor scripts in sequence.\n# Schedule: 0 7 * * 0 (Sunday 7am — after weekly_sync_maintenance.sh finishes at ~3am)\n# Each script runs independently and notifies on its own findings.\n COFFEE_REPORT_SCRIPTS=(\n \"Monitors/zfs_memory_snapshot.sh\" # ZFS pool health + ARC + Docker memory snapshot\n \"Monitors/smart_health.sh\" # drive SMART attributes — reallocated, pending, temp\n \"Monitors/cert_monitor.sh\" # SSL certificate expiry for all configured domains\n \"Monitors/backup_verify.sh\" # rsync mirror integrity via independent MD5 checksums\n \"Monitors/bandwidth_monitor.sh\" # weekly rsync transfer totals and per-share breakdown\n \"Monitors/emby_session_report.sh\" # Emby usage — streams, users, library, transcode ratio\n \"Monitors/weekly_health_digest.sh\" # aggregated digest — watchdogs, fallback, skip list\n )\n\n# ==============================================================================================\n# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Rsync Enable/Disable ━━━\n# Two-tier toggle system — Tier 1 overrides Tier 2.\n#\n# Tier 1 — Global gate:\n# RSYNC_ENABLED=false → ALL rsync stops everywhere, no exceptions\n# Use when: remote completely offline, major maintenance, disaster recovery\n#\n# Tier 2 — Per-orchestrator (only applies when Tier 1 is true):\n# Fine grained control — disable specific orchestrators while keeping others\n# Use when: rebuilding secondary, testing, per-window bandwidth management\n#\n# Example — HOST2 data rebuild:\n# RSYNC_ENABLED=true ← rsync works, individual scripts run fine\n# CRITICAL_RSYNC_ENABLED=true ← 30min auth stack sync still runs\n# INTERMEDIATE_RSYNC_ENABLED=false ← skip 4h arr/mid-day rsync during rebuild\n# DAILY_RSYNC_ENABLED=false ← skip daily HDD syncs during rebuild\n# WEEKLY_RSYNC_ENABLED=true ← Emby + Critical-Data still sync (NVMe)\n# FALLBACK_RSYNC_ENABLED=true ← handback writeback still works\n# → Run individual: bash Rsync/rsync.sh /mnt/user/Movies\n# → When ready: INTERMEDIATE_RSYNC_ENABLED=true DAILY_RSYNC_ENABLED=true\n RSYNC_ENABLED=true # Tier 1 — global gate, overrides everything below\n CRITICAL_RSYNC_ENABLED=true # Tier 2 — disabled during HOST2 rebuild, re-enable when ready\n INTERMEDIATE_RSYNC_ENABLED=true # Tier 2 — intermediate_sync_maintenance.sh rsync section\n DAILY_RSYNC_ENABLED=false # Tier 2 — HOST2 rebuild in progress, re-enable when ready\n WEEKLY_RSYNC_ENABLED=true # Tier 2 — weekly_sync_maintenance.sh rsync section\n FALLBACK_RSYNC_ENABLED=true # Tier 2 — fallback.sh writeback jobs on handback\n\n# ━━━ Rsync Defaults ━━━\n# Global fallback values used when no profile match is found.\n# Media shares in HOST*_DAILY_SYNC_SHARES always use these globals — no profile needed.\n# Appdata shares match profiles by directory basename (lowercased).\n BW_LIMIT=12500 # KB/s — 12500 ≈ 100Mbit\n RETRY_COUNT=3 # retry attempts before giving up\n SLEEP=300 # seconds between retry attempts\n CRITICAL_CONTAINER_NAMES=() # containers stopped on REMOTE before rsync — profiles override\n DELAYED_CONTAINERS=() # containers needing delay before starting — profiles override\n CONTAINER_DELAY=5 # seconds before starting delayed containers\n EXCLUDE_DIRS=() # directories excluded from transfer — profiles override\n\n# --inplace writes directly to destination — delta against existing file, better for large media\n# --partial keep partial file on interrupted transfer so next run resumes, not re-transfers\n# --timeout kill stalled transfers instead of hanging indefinitely\n# --numeric-ids use UIDs/GIDs numerically — prevents ownership mismatches between servers\n# --delete intentionally omitted — arr_cleanup.sh enforces media truth post-rsync.\n# Media shares use this default; rsync spreads files only, never removes them.\n# Note: --no-whole-file removed — redundant over SSH (delta transfer is already the default).\n# Note: arr_cleanup only catches true orphans under the union model — intentional removals\n# require arr_sync.sh --blocklist-add first, then manual file deletion.\n DEFAULT_RSYNC_OPTS=(-av --info=progress2 --human-readable --bwlimit=\"$BW_LIMIT\" --inplace --partial --timeout=60 --numeric-ids)\n\n# ━━━ Remote Health Checks ━━━\n# Pre-flight — aborts if remote rootfs (/) usage is at or above this percentage.\n# When remote array is down, rsync writes land on rootfs and fill it rapidly.\n ROOTFS_WARN=75\n\n# ━━━ Rsync Profile System ━━━\n# Profiles allow per-share rsync behaviour without touching script logic.\n# Profile key matched by basename of directory passed to rsync.sh (lowercased).\n# Override with --profile=name flag.\n#\n# IMPORTANT: PROFILE_RSYNC_OPTS does NOT inherit DEFAULT_RSYNC_OPTS.\n# List ALL desired options explicitly when defining a profile.\n#\n# Current profiles:\n# arrs_stack — arr databases — lower bandwidth, containers stopped for consistency\n# critical-data — auth stack — full stop both sides, Authelia delayed start\n# called by weekly_sync_maintenance.sh — full clean sync weekly\n# critical-fallback — dirty sync — auth stays running both sides, WAL excluded\n# called by critical_sync_maintenance.sh every 30min\n# host1-appdata — HOST1 server-specific appdata — defined in host1.conf\n# host2-appdata — HOST2 server-specific appdata — defined in host2.conf\n# important-data — NextCloud + Postgres — NextCloud delayed start after Postgres\n# emby — weekly clean sync — both Emby stopped, full mirror\n# called by weekly_sync_maintenance.sh only — do NOT schedule separately\n# emby-fallback — dirty sync — Emby stays running, WAL excluded\n# called by critical_sync_maintenance.sh every 30min\n\n declare -A PROFILE_RSYNC_OPTS=(\n [arrs_stack]=\"-av --info=progress2 --human-readable --bwlimit=$BW_LIMIT --delete --inplace\"\n [critical-data]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete\"\n [critical-fallback]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete --inplace --no-whole-file\"\n [important-data]=\"-av --human-readable --bwlimit=$BW_LIMIT\"\n [emby]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete --inplace --no-whole-file\"\n [emby-fallback]=\"-av --human-readable --bwlimit=$BW_LIMIT --delete --inplace --no-whole-file\"\n )\n\n# Per-profile bandwidth limits in KB/s\n declare -A PROFILE_BW_LIMIT=(\n [arrs_stack]=5000 # lower — runs alongside other syncs\n [critical-data]=9500 # high — small dataset, sync fast\n [critical-fallback]=9500 # high — small dataset, sync fast\n [important-data]=9500 # high — database sync\n [emby]=8000 # medium — large full mirror\n [emby-fallback]=9500 # high — small critical dataset\n )\n\n# Retry attempts per profile\n declare -A PROFILE_RETRY_COUNT=(\n [arrs_stack]=3\n [critical-data]=3\n [critical-fallback]=3\n [important-data]=3\n [emby]=3\n [emby-fallback]=3\n )\n\n# Seconds between retry attempts\n declare -A PROFILE_SLEEP=(\n [arrs_stack]=300\n [critical-data]=300\n [critical-fallback]=120 # shorter — frequent dirty sync, retry faster\n [important-data]=300\n [emby]=300\n [emby-fallback]=120 # shorter — frequent dirty sync, retry faster\n )\n\n# Containers stopped on BOTH LOCAL and REMOTE before rsync.\n# Local stops first — flushes databases cleanly. Remote stops next — prevents writes.\n# Only running containers get restarted — stopped containers stay stopped.\n# SPACE-SEPARATED STRINGS — converted to array at runtime\n declare -A PROFILE_CRITICAL_CONTAINER_NAMES=(\n [arrs_stack]=\"Sonarr Lidarr Readarr Radarr Prowlarr Bazarr Pinchflat\"\n [critical-data]=\"Mariadb-Authelia Mariadb-Authelia-Secondary Redis-Authelia Redis-Authelia-Secondary Lldap-Gmer4Lfe NginxProxyManager Authelia Authelia-Secondary\"\n [critical-fallback]=\"\" # dirty sync — auth stays running both sides\n [important-data]=\"Postgres-NextCloud NextCloud\"\n [emby]=\"Emby\"\n [emby-fallback]=\"\" # dirty sync — Emby stays running both sides\n )\n\n# Containers needing a delay after rsync before starting.\n# SPACE-SEPARATED STRINGS — converted to array at runtime\n declare -A PROFILE_DELAYED_CONTAINERS=(\n [arrs_stack]=\"\"\n [critical-data]=\"Authelia Authelia-Secondary\" # wait for Mariadb + Redis\n [critical-fallback]=\"\"\n [important-data]=\"NextCloud\" # wait for Postgres\n [emby]=\"\"\n [emby-fallback]=\"\"\n )\n\n# Seconds before starting delayed containers\n declare -A PROFILE_CONTAINER_DELAY=(\n [arrs_stack]=5\n [critical-data]=15 # Mariadb + Redis need time to accept connections\n [critical-fallback]=5\n [important-data]=10 # Postgres needs time before NextCloud\n [emby]=5\n [emby-fallback]=5\n )\n\n# Directories excluded from rsync per profile.\n# SPACE-SEPARATED STRINGS — converted to array at runtime\n declare -A PROFILE_EXCLUDE_DIRS=(\n [arrs_stack]=\"logs *.tmp\"\n [critical-data]=\"logs *.tmp *.log nginx/temp nginx/cache __pycache__ notification.txt\"\n [critical-fallback]=\"logs *.tmp *.log nginx/temp nginx/cache __pycache__ notification.txt *.db-wal *.db-shm\"\n [important-data]=\"logs *.tmp\"\n [emby]=\"logs transcodes cache crash*\"\n [emby-fallback]=\"logs transcodes cache metadata *.db-wal *.db-shm crash* plugins root\"\n )\n\n# Remote restart after dirty sync — restart these on remote IF they were running before sync.\n# Same logic as stop/start — was stopped = stays stopped, was running = gets restarted.\n# Used by dirty sync profiles (critical-fallback, emby-fallback) so remote picks up changes.\n# SPACE-SEPARATED STRINGS — converted to array at runtime\n declare -A PROFILE_REMOTE_RESTART_CONTAINERS=(\n [critical-fallback]=\"NginxProxyManager Authelia Authelia-Secondary Lldap-Gmer4Lfe Mariadb-Authelia Mariadb-Authelia-Secondary Redis-Authelia Redis-Authelia-Secondary\"\n [emby-fallback]=\"Emby\"\n )\n\n# Note: disk check is auto-detected from disks.ini — no PROFILE_SKIP_DISK_CHECK needed.\n# check_remote_disks() reads fsType per disk and handles XFS, ZFS, and cache pools automatically.\n\n# ==============================================================================================\n# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Mutual container failover between two unRAID servers.\n# Each server runs Fallback/fallback.sh independently via array_started.sh.\n# All decisions based on two pings: remote reachable + internet reachable.\n#\n# States: NORMAL | FALLBACK | NO_INTERNET | DARK\n#\n# DDNS rules — absolute:\n# Internet loss → stop own DDNS immediately\n# Failover → start remote DDNS first (Tier 1)\n# Handback → stop remote DDNS → rsync → start containers → start local DDNS last\n#\n# Per-host container lists and tier delays live in host*.conf.\n# Shared settings (intervals, state file, thresholds) live here.\n\n EXTERNAL_IP=\"8.8.8.8\"\n FALLBACK_CHECK_INTERVAL=30 # seconds between fallback state checks\n FALLBACK_HANDBACK_STRIKES=3 # consecutive healthy checks before initiating handback (3×30s = 90s)\n FALLBACK_STATE_FILE=\"$STATE_DIR/fallback_state.db\"\n FALLBACK_ENABLED=true # HOST2 back online\n # false = suppresses \"not running\" warnings in status scripts\n FALLBACK_PARTNERSHIP_REQUIRED=true # gate fallback on an active partnership\n # false = standalone mode, no partnership dependency\n FALLBACK_PARTNERSHIP_SUSPEND_AFTER=120 # minutes without active partnership before suspending\n # 0 = suspend immediately when partnership goes inactive\n\n# ━━━ Failover Test ━━━\n# Controlled simulation of a failover event — run manually via fallback_test.sh.\n FALLBACK_TEST_BLOCK_WAIT=150 # seconds to wait after blocking connectivity\n FALLBACK_TEST_HANDBACK_WAIT=360 # seconds to wait before initiating handback\n\n# ==============================================================================================\n# ── DOCKER ESSENTIALS ─────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Downloaders Reset ━━━\n# Runs every 30 minutes via CRITICAL_MAINTENANCE_SCRIPTS.\n# Clears stuck states, purges old history, prepares each download client for a clean cycle.\n# Per-host URLs and API keys live in host*.conf.\n DOWNLOADER_RETENTION_DAYS=7 # days — purge history older than this\n\n# qBittorrent failsafe — removes torrents older than threshold regardless of ratio\n# deleteFiles=false — removes from qBit but leaves files on disk (arr manages files)\n QBIT_FAILSAFE_MIN_DAYS=180 # days — minimum age before failsafe deletion\n QBIT_FAILSAFE_MIN_RATIO=0 # 0 = age only, no ratio requirement\n\n# ━━━ Docker Daily Restart ━━━\n# Containers restarted every day via DAILY_MAINTENANCE_SCRIPTS.\n# Per-host lists live in host*.conf:\n# HOST1_DAILY_RESTART_CONTAINERS\n# HOST2_DAILY_RESTART_CONTAINERS\n# detect_hosts() sets DAILY_RESTART_CONTAINERS to the correct host array at runtime.\n\n# ━━━ Docker Weekly Restart ━━━\n# Less critical services restarted weekly via WEEKLY_MAINTENANCE_SCRIPTS (Sunday 2:30am).\n# Containers already stopped for weekly sync — restart adds zero extra downtime.\n# Per-host lists live in host*.conf:\n# HOST1_WEEKLY_RESTART_CONTAINERS\n# HOST2_WEEKLY_RESTART_CONTAINERS\n\n# ━━━ Docker Watchdog ━━━\n# Continuous two-tier self-healing container monitoring.\n# Started by array_started.sh — runs until array stops.\n# Re-sources all three conf files each cycle — add/remove containers without restarting watchdog.\n#\n# Tier 1 — strict monitoring of explicitly configured containers:\n# Memory hard limits — immediate restart if exceeded\n# CPU thresholds — strike system, restart after CPU_FAIL_LIMIT sustained strikes\n# HTTP responsiveness — strike system, restart after RESP_FAIL_LIMIT failed checks\n# Required containers — must always be running, strike + skip list with auto-clear\n#\n# Tier 2 — global health scan of ALL running containers:\n# Unhealthy status — Docker HEALTHCHECK unhealthy → restart\n# OOM killed — kernel killed → restart + notify\n# Crash loop detection — RestartCount climbing → notify, critical above limit\n# Dead containers — remove and restart\n# Unexpected exits — non-zero exit code → restart\n#\n# All per-host container lists live in host*.conf:\n# HOST*_WATCHDOG_CONTAINERS — memory hard limits per container\n# HOST*_WATCHDOG_CONTAINER_URLS — HTTP health check URLs\n# HOST*_WATCHDOG_REQUIRED_CONTAINERS — must always be running\n# HOST*_WATCHDOG_SCAN_IGNORE — skip in Tier 2 scan\n# HOST*_WATCHDOG_DEPENDENCIES — dependency ordering for restart decisions\n\n# Strike state file — persistent in STATE_DIR\n WATCHDOG_STATE_FILE=\"$STATE_DIR/container_watchdog_state.db\"\n\n# CPU thresholds — normalised against total core count at runtime\n SOFT_CPU_THRESHOLD=80 # warn at this % of total system CPU\n HARD_CPU_THRESHOLD=85 # strike at this % of total system CPU\n CPU_FAIL_LIMIT=2 # consecutive hard CPU strikes before container restart\n\n# Memory soft threshold — warn when container reaches this % of its hard limit\n SOFT_MEM_THRESHOLD=80\n\n# HTTP responsiveness\n RESP_FAIL_LIMIT=2 # consecutive failed checks before restart\n CURL_TIMEOUT=5 # seconds per check before timeout\n\n# Tier 2 master toggle\n WATCHDOG_SCAN_ALL=true # false = only WATCHDOG_CONTAINERS + required containers\n\n# Individual Tier 2 check toggles\n WATCHDOG_RESTART_UNHEALTHY=true\n WATCHDOG_RESTART_DEAD=true\n WATCHDOG_RESTART_CRASHED=true\n WATCHDOG_NOTIFY_OOM=true\n WATCHDOG_NOTIFY_CRASHLOOP=true\n\n# Crash loop threshold\n WATCHDOG_CRASH_LIMIT=5 # RestartCount above this = critical crash loop\n\n# Startup grace period — skip restarts while system is still booting\n WATCHDOG_STARTUP_GRACE=600 # seconds after boot before watchdog acts on failures\n\n# Restart loop protection — prevents watchdog from endlessly restarting a broken container\n WATCHDOG_CONTAINER_RESTART_LIMIT=3\n WATCHDOG_CONTAINER_RESTART_WINDOW=1 # rolling window in hours\n WATCHDOG_CONTAINER_RESTART_LOG=\"$DATA_DIR/container_restart_history.db\"\n\n# Notification batching — one summary per cycle instead of one ping per event\n WATCHDOG_BATCH_NOTIFY=true\n\n# Appdata size monitoring — two-part catch-all for runaway growth and oversized log files.\n#\n# Part 1 — Growth rate (zero-config):\n# Reads per-container dir totals each cycle via du, compares to previous cycle.\n# Any container growing more than WATCHDOG_APPDATA_GROWTH_GB triggers a focused *.log scan\n# inside that container. No per-container config required — new containers are covered\n# automatically. Baseline built on first run after boot; growth detection starts cycle 2.\n#\n# Part 2 — Absolute log size:\n# Finds *.log / *.log.* files over WATCHDOG_APPDATA_LOG_MAX_GB across all appdata paths.\n# Catches logs that have already stabilised at a large size and are no longer actively growing.\n#\n# Strike system (reuses existing watchdog infrastructure):\n# Strike 1 — warn + notify: condition first detected\n# Strike 2 — warn + escalated notify: still present next cycle\n# Strike 3 (WATCHDOG_APPDATA_STRIKE_LIMIT) — action cycle:\n# If WATCHDOG_APPDATA_TRUNCATE_LOGS=true: truncate *.log files in-place, clear strikes\n# If false: critical notify only, strikes held until condition resolves\n# Condition resolves (growth stops / log drops below threshold) → strikes auto-clear\n#\n# HOST*_WATCHDOG_APPDATA_SIZES (in host*.conf) suppresses growth warnings for a\n# container until its dir exceeds the configured ceiling. Only needed when a container\n# legitimately has large stable data and you want to guarantee it never triggers a false alarm.\n WATCHDOG_CHECK_APPDATA=true\n WATCHDOG_APPDATA_PATHS=(\"/mnt/docker-unraid/appdata\")\n WATCHDOG_APPDATA_GROWTH_GB=2 # flag containers growing more than this per cycle\n WATCHDOG_APPDATA_LOG_MAX_GB=2 # flag *.log files exceeding this size (absolute)\n WATCHDOG_APPDATA_TRUNCATE_LOGS=false # set true to auto-truncate oversized *.log files on action cycle\n WATCHDOG_APPDATA_STRIKE_LIMIT=3 # cycles before action fires (matches existing watchdog pattern)\n WATCHDOG_APPDATA_GROWTH_FILE=\"$STATE_DIR/watchdog_appdata_growth.db\"\n STORAGE_WATCHDOG_STATE_FILE=\"$STATE_DIR/storage_watchdog_state.db\"\n\n# ━━━ Docker Network Connect ━━━\n# Ensures custom networks exist and connects containers at array start.\n# Runs once via ARRAY_START_SCRIPTS — idempotent, safe to re-run.\n# Container and network lists are host-specific — defined in host*.conf:\n# HOST1_NETWORK_CONNECT_CONTAINERS / HOST2_NETWORK_CONNECT_CONTAINERS\n# HOST1_NETWORK_CONNECT_NETWORKS / HOST2_NETWORK_CONNECT_NETWORKS\n\n# ==============================================================================================\n# ── UNRAID ESSENTIALS ─────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ inotify Tuning ━━━\n# Linux inotify limits — applied at every array start by inotify_tuning.sh.\n# Default unRAID values are very low — with many Docker containers watching files\n# you can silently exhaust the limit causing containers to miss file events.\n# Symptoms: containers miss file events, downloads not detected, library not updated.\n# Live TV stutter is a known symptom of inotify exhaustion.\n# These settings are lost on reboot — reapplied automatically at array start.\n INOTIFY_MAX_INSTANCES=1024 # default: 128 — max inotify instances per user\n # 1024 handles ~20-30 containers watching files simultaneously\n INOTIFY_MAX_WATCHES=1048576 # default: 8192 — SHARED budget across ALL users/containers\n # 524288 (512K) was previous value — raised to 1M (1048576)\n # VSCode/Code-Server alone needs ~50K-200K for large workspaces\n # with node_modules. All arr containers + Emby + VSCode share\n # this budget. 1M is safe on 128GB RAM (~128MB kernel memory)\n # If VSCode shows \"unable to watch for file changes\" → too low\n INOTIFY_MAX_QUEUED_EVENTS=32768 # default: 16384 — max events queued before dropping\n\n# ━━━ System Tuning Monitor ━━━\n# Tracks inotify and php-fpm usage over time.\n# Snapshot written every 6 hours by system_tuning_monitor.sh.\n# Read by sunday_morning_coffee_report.sh for weekly peak/avg/warning summary.\n INOTIFY_WARN_PCT=80 # warn if inotify instances exceed this % of limit\n PHP_FPM_WARN_PCT=80 # warn if php-fpm workers exceed this % of max_children\n TUNING_MONITOR_LOG=\"$DATA_DIR/system_tuning_history.db\"\n TUNING_LOG_RETENTION=30 # days before old entries are purged\n\n# ━━━ Reboot ━━━\n# Seconds of warning broadcast to logged-in users before server_reboot.sh reboots.\n# Gives users time to save work — 300s = 5 minutes.\n REBOOT_SLEEP=300\n\n# ━━━ Mover ━━━\n# Seconds to wait before mover_stop.sh sends SIGTERM to the mover process.\n# Gives mover time to finish current file transfer before being interrupted.\n MOVER_STOP_TIMEOUT=300\n\n# ━━━ Syslog Filter ━━━\n# Path for the rsyslog filter file that suppresses Docker veth interface noise.\n# Docker creates a new veth interface for each container — generates hundreds of\n# log lines per hour that have no diagnostic value. Filter removes them at source.\n FILTER_FILE=\"/etc/rsyslog.d/ignore-docker-veth.conf\"\n\n# ━━━ PHP-FPM ━━━\n# Higher max_children allows more concurrent PHP requests to the unRAID WebGUI.\n# Default is very low — increasing it prevents WebGUI slowdowns under load.\n# 250 is safe for servers with 32GB+ RAM.\n PHP_CONF=\"/etc/php-fpm.d/www.conf\"\n PHP_MAX_CHILDREN=250\n\n# ━━━ Clear Logs ━━━\n# System log files cleared weekly to prevent rootfs fill over time.\n# These grow continuously — without clearing they eventually consume all rootfs space.\n# clear_logs.sh runs in WEEKLY_MAINTENANCE_SCRIPTS — Sunday 2:30am.\n#\n# Size threshold approach — only clear if log exceeds threshold.\n# Avoids destroying useful recent diagnostic context when logs are small.\n# LOG_MIN_SIZE_MB: skip clearing if log is under this size (not worth clearing)\n# LOG_DOCKER_MAX_MB: clear a container log only if it exceeds this size\n# Docker logs grow fastest on active containers (Emby, SABnzbd, Sonarr)\n# 100MB per container × 30 containers = 3GB before clearing kicks in\n LOG_FILES=(/var/log/syslog /var/log/messages /var/log/dmesg)\n LOG_MIN_SIZE_MB=10 # skip system log if under this size (already small)\n LOG_DOCKER_MAX_MB=100 # clear Docker container log only if over this size (MB)\n\n# ━━━ Network Watchdog ━━━\n# Services-layer connectivity — internet reachability, DDNS sync, Tailscale, NPM proxy.\n# Host-specific values (domain, container, NPM URL) live in host*.conf.\n NETWORK_WATCHDOG_ENABLED=true\n NETWORK_WATCHDOG_INTERNET_URL=\"https://1.1.1.1\"\n NETWORK_WATCHDOG_INTERNET_TIMEOUT=5\n NETWORK_WATCHDOG_CHECK_TAILSCALE=true\n NETWORK_WATCHDOG_NPM_TIMEOUT=10\n NETWORK_WATCHDOG_NPM_STRIKE_LIMIT=2\n NETWORK_WATCHDOG_NPM_STATE_FILE=\"$STATE_DIR/network_watchdog_state.db\"\n\n# ━━━ WebGUI Watchdog ━━━\n# Monitors unRAID WebGUI responsiveness — escalates through nginx restart → emhttp restart.\n# Separate from docker_watchdog — this monitors the unRAID UI itself, not containers.\n WEBGUI_URL=\"http://localhost\"\n WEBGUI_TIMEOUT=5 # seconds before curl gives up on WebGUI check\n WEBGUI_NGINX_WAIT=15 # seconds after nginx restart before rechecking\n WEBGUI_EMHTTP_WAIT=30 # seconds after emhttp restart before rechecking\n\n# ==============================================================================================\n# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Media Permissions ━━━\n# Applied recursively by media_shares_permissions.sh.\n# Runs first in DAILY_MAINTENANCE_SCRIPTS — arr cleanup depends on correct ownership.\n#\n# Why split directory vs file permissions:\n# Directories need execute bit to enter — 755 allows owner+group+others to traverse\n# Files should NOT be executable — 664 allows owner+group read/write, others read\n# This is POSIX best practice for media servers, not a blanket 777 band-aid\n#\n# Why nobody:users ownership:\n# All ecosystem containers run as PUID=99 (nobody) PGID=100 (users) on unRAID\n# Files owned by nobody:users are accessible to all containers without world-write\n# If this script fixes many files each run → a container has wrong PUID/PGID set\n# Fix: add PUID=99 PGID=100 to the container's environment variables\n#\n# This script runs daily as a failsafe — even with correct container config:\n# rsync may bring files with source ownership if not run with --chown\n# Manual admin copies create root:root files\n# New containers may not have PUID/PGID set yet\n PERMISSIONS_DIR_MODE=\"755\" # directories — traverse + list, no world-write\n PERMISSIONS_FILE_MODE=\"664\" # files — group read/write, no execute\n PERMISSIONS_OWNER=\"nobody:users\"\n\n# Share list defined per host in host*.conf:\n# HOST1_MEDIA_PERMISSION_SHARES / HOST2_MEDIA_PERMISSION_SHARES\n\n# ━━━ Media Cleaner ━━━\n# Removes junk files from media shares — two profiles: anime and media.\n# Called via DAILY_MAINTENANCE_SCRIPTS. Run manually: Media/media_cleaner.sh anime|media\n# Folder lists defined per host in host*.conf:\n# HOST1_ANIME_CLEAN_FOLDERS / HOST2_ANIME_CLEAN_FOLDERS\n# HOST1_MEDIA_CLEAN_FOLDERS / HOST2_MEDIA_CLEAN_FOLDERS\n\n ANIME_FILE_PATTERNS=(\n # ── Checksums ────────────────────────────────────────────────────────────────────────\n '*.sfv' '*.md5' '*.sha1' # verification files — useless post-download\n\n # ── Scene / download metadata ─────────────────────────────────────────────────────────\n '*.url' '*.lnk' # scene links\n '*.info' '*.diz' # scene description files\n '*.nzb' # usenet download files\n '*.torrent' # torrent files left by download clients\n\n # ── Archives and segments ─────────────────────────────────────────────────────────────\n '*.rar' '*.zip' '*.7z' '*.ace' # archives — source files not needed after extract\n '*.r00' '*.r01' '*.r02' '*.r03' # multi-part rar segments\n '*.r04' '*.r05' '*.r06' '*.r07'\n '*.r08' '*.r09' '*.srr' # scene repair files\n '*.001' '*.002' '*.003' # split archive parts\n '*.gz' '*.tar' '*.bz2' # linux archives\n\n # ── Scene samples and proofs ──────────────────────────────────────────────────────────\n '*.sample*' '*.proof*' # scene samples — never needed in library\n\n # ── Executables and scripts ───────────────────────────────────────────────────────────\n '*.exe' '*.scr' '*.com' # Windows executables\n '*.bat' '*.cmd' '*.vbs' '*.ps1' # Windows scripts\n '*.msi' '*.dll' '*.sys' # Windows installers and system files\n '*.sh' # shell scripts in media folders = suspicious\n\n # ── Incomplete downloads ──────────────────────────────────────────────────────────────\n '*.!ut' '*.!qB' # uTorrent / qBittorrent incomplete markers\n '*.crdownload' '*.opdownload' # Chrome / Opera incomplete downloads\n '*.part' # partial download files\n\n # ── Sync conflicts ────────────────────────────────────────────────────────────────────\n '*sync-conflict*' # Syncthing conflict copies\n )\n\n MEDIA_FILE_PATTERNS=(\n # ── Checksums ────────────────────────────────────────────────────────────────────────\n '*.sfv' '*.md5' '*.sha1' # verification files — useless post-download\n\n # ── Scene / download metadata ─────────────────────────────────────────────────────────\n '*.url' '*.lnk' # scene links\n '*.info' '*.diz' # scene description files\n '*.nzb' # usenet download files\n '*.torrent' # torrent files left by download clients\n\n # ── Archives and segments ─────────────────────────────────────────────────────────────\n '*.rar' '*.zip' '*.7z' '*.ace' # archives — source files not needed after extract\n '*.r00' '*.r01' '*.r02' '*.r03' # multi-part rar segments\n '*.r04' '*.r05' '*.r06' '*.r07'\n '*.r08' '*.r09' '*.srr' # scene repair files\n '*.001' '*.002' '*.003' # split archive parts\n '*.gz' '*.tar' '*.bz2' # linux archives\n\n # ── Scene samples and proofs ──────────────────────────────────────────────────────────\n '*.sample*' '*.proof*' # scene samples — never needed in library\n\n # ── Executables and scripts ───────────────────────────────────────────────────────────\n '*.exe' '*.scr' '*.com' # Windows executables\n '*.bat' '*.cmd' '*.vbs' '*.ps1' # Windows scripts\n '*.msi' '*.dll' '*.sys' # Windows installers and system files\n '*.sh' # shell scripts in media folders = suspicious\n\n # ── Incomplete downloads ──────────────────────────────────────────────────────────────\n '*.!ut' '*.!qB' # uTorrent / qBittorrent incomplete markers\n '*.crdownload' '*.opdownload' # Chrome / Opera incomplete downloads\n '*.part' # partial download files\n\n # ── Sync conflicts ────────────────────────────────────────────────────────────────────\n '*sync-conflict*' # Syncthing conflict copies\n\n # ── Media-specific extras ─────────────────────────────────────────────────────────────\n '*.iso' # disc images — extracted content is in library\n '*.lrc' # lyrics files — handled by music apps not Emby\n )\n\n# ━━━ Arr Cleanup ━━━\n# Orphan file cleanup via Lidarr, Sonarr, and Radarr APIs.\n# Per-host URLs, API keys, and path maps live in host*.conf.\n# detect_hosts() selects correct host vars at runtime.\n#\n# API versions — update MAJOR version here when script is updated for a new arr version:\n# Sonarr v4 → /api/v3/series → /api/v3/episodefile?seriesId=X\n# Radarr v6 → /api/v3/movie → /api/v3/moviefile?movieId=X\n# Lidarr v3 → /api/v1/artist → /api/v1/trackFile?artistId=X\n SONARR_VERSION_MAJOR=4\n RADARR_VERSION_MAJOR=6\n LIDARR_VERSION_MAJOR=3\n\n# Lidarr shared settings\n LIDARR_LOCK_WARN_AGE=3600 # 1hr — large libraries take time, not stuck\n LIDARR_ORPHAN_AGE=7 # days — files must be older than this before eligible for deletion\n LIDARR_MAX_DELETE_GB=5 # require --i-know-what-im-doing if deletion exceeds this\n LIDARR_MIN_TRACKED_PCT=80 # abort if tracked count drops below this % of last run\n # protects against API returning partial data on a bad day\n LIDARR_TRACKED_COUNT_FILE=\"$DATA_DIR/lidarr_tracked.count\"\n LIDARR_EXTENSIONS=(\"flac\" \"mp3\" \"m4a\" \"wav\" \"aac\" \"ogg\" \"opus\" \"wma\")\n LIDARR_PROTECTED_PATTERNS=(\n # Metadata\n \"*.nfo\" \"*.tbn\"\n # Images — album art, artist images, Emby artwork\n \"*.jpg\" \"*.jpeg\" \"*.png\" \"*.webp\" \"*.svg\"\n \"poster.*\" \"fanart.*\" \"backdrop.*\" \"clearlogo.*\"\n \"banner.*\" \"thumb.*\" \"landscape.*\"\n \"folder.*\" \"cover.*\" \"album.*\" \"artist.*\" \"disc.*\"\n # Lyrics\n \"*.lrc\"\n )\n\n# Lidarr artwork fetcher settings\n LIDARR_ART_MIN_SIZE=10000 # bytes — reject downloads smaller than this\n LIDARR_ART_MAX_PARALLEL=4 # concurrent background download jobs\n LIDARR_ART_RETRIES=2 # download retry attempts per image\n LIDARR_ART_SLEEP_BETWEEN=0.2 # seconds between fanart.tv API calls\n# HOST*_FANART_API_KEY / HOST*_LASTFM_API_KEY — set in host*.conf\n\n# Lidarr discovery settings (playback_aware_lidarr_discovery.sh)\n LIDARR_DISCOVERY_THRESHOLD=70 # score to accept candidate (0-100)\n LIDARR_DISCOVERY_LOOKBACK_DAYS=7 # Emby play history window in days\n LIDARR_DISCOVERY_MIN_PLAYS=3 # min plays in window before evaluating an artist\n LIDARR_DISCOVERY_USER_CAP_PCT=35 # max % any single user can contribute to play score (prevents one listener dominating)\n LIDARR_DISCOVERY_MAX_ADDS=5 # max artists to add per run — quality over bulk\n LIDARR_DISCOVERY_REJECT_COOLDOWN=30 # days before re-evaluating a rejected artist\n LIDARR_DISCOVERY_HISTORY=\"$DATA_DIR/lidarr_discovery_history.db\"\n\n# Sonarr discovery settings (playback_aware_sonarr_discovery.sh)\n SONARR_DISCOVERY_THRESHOLD=52 # score to accept candidate (0-100)\n SONARR_DISCOVERY_LOOKBACK_DAYS=14 # Emby episode play history window in days (shorter than movies — TV watched more frequently)\n SONARR_DISCOVERY_MAX_SEEDS=5 # max seed series from Stage 1\n SONARR_DISCOVERY_MAX_ADDS=3 # max shows to add per run — TV is a larger commitment than movies\n SONARR_DISCOVERY_MIN_VOTE_COUNT=50 # min TMDB votes (TV has fewer votes than movies at same popularity)\n SONARR_DISCOVERY_MIN_RATING=65 # min TMDB vote_average × 10 (65 = 6.5/10)\n SONARR_DISCOVERY_REJECT_COOLDOWN=60 # days before re-evaluating a rejected show\n SONARR_DISCOVERY_USER_EPISODE_CAP=8 # max episodes any one user contributes to seed volume score\n SONARR_DISCOVERY_MONITOR_MODE=\"all\" # Sonarr monitor mode on add: all | future | first | latest | none\n SONARR_DISCOVERY_HISTORY=\"$DATA_DIR/sonarr_discovery_history.db\"\n\n# Radarr discovery shared settings\n RADARR_DISCOVERY_THRESHOLD=52 # score to accept candidate (0-100) — lower than Lidarr since diverse seeds rarely overlap\n RADARR_DISCOVERY_LOOKBACK_DAYS=30 # Emby watch history window in days (movies rewatched less often)\n RADARR_DISCOVERY_MAX_SEEDS=5 # max seed movies from Stage 1\n RADARR_DISCOVERY_MAX_ADDS=5 # max movies to add per run\n RADARR_DISCOVERY_MIN_VOTE_COUNT=100 # min TMDB votes to be considered a candidate\n RADARR_DISCOVERY_MIN_RATING=60 # min TMDB vote_average × 10 (60 = 6.0/10)\n RADARR_DISCOVERY_REJECT_COOLDOWN=60 # days before re-evaluating a rejected movie\n RADARR_DISCOVERY_SEED_LIBRARIES=(\"Movies\") # Emby libraries to draw seed movies from\n RADARR_DISCOVERY_HISTORY=\"$DATA_DIR/radarr_discovery_history.db\"\n\n# Emby → arr sync library allowlists\n# Only these Emby library names will be considered by the sync tools.\n# Names must match exactly as shown in Emby > Dashboard > Libraries.\n SONARR_EMBY_LIBRARIES=(\"Anime\" \"Kids Shows\" \"Stand-Up Comedy\" \"TV shows\")\n RADARR_EMBY_LIBRARIES=(\"Movies\" \"Kid's Movies\")\n\n# Sonarr shared settings\n SONARR_ORPHAN_AGE=7 # days — files must be older than this before eligible for deletion\n SONARR_MAX_DELETE_GB=10 # require --i-know-what-im-doing if deletion exceeds this\n SONARR_EXTENSIONS=(\"mkv\" \"mp4\" \"avi\" \"m4v\" \"ts\" \"wmv\" \"mov\")\n SONARR_PROTECTED_PATTERNS=(\n # Subtitles\n \"*.srt\" \"*.sub\" \"*.ass\" \"*.ssa\" \"*.idx\" \"*.vtt\"\n # Metadata\n \"*.nfo\" \"*.tbn\"\n # Images\n \"*.jpg\" \"*.jpeg\" \"*.png\" \"*.webp\" \"*.svg\"\n \"poster.*\" \"fanart.*\" \"backdrop.*\" \"clearlogo.*\"\n \"banner.*\" \"thumb.*\" \"landscape.*\"\n # Kodi/Emby extras — not tracked by Sonarr API\n \"*-trailer.*\" \"*-featurette.*\" \"*-behindthescenes.*\"\n \"*-interview.*\" \"*-scene.*\" \"*-short.*\" \"*-deleted.*\"\n \"*-clip.*\" \"*-other.*\"\n # Theme songs — stored in show folder, not tracked by arr\n \"theme.mp3\" \"theme.flac\" \"theme.wav\" \"theme.m4a\" \"theme.mka\"\n )\n\n# Radarr shared settings\n RADARR_ORPHAN_AGE=7 # days — files must be older than this before eligible for deletion\n RADARR_MAX_DELETE_GB=15 # require --i-know-what-im-doing if deletion exceeds this\n RADARR_EXTENSIONS=(\"mkv\" \"mp4\" \"avi\" \"m4v\" \"wmv\" \"mov\")\n RADARR_PROTECTED_PATTERNS=(\n # Subtitles\n \"*.srt\" \"*.sub\" \"*.ass\" \"*.ssa\" \"*.idx\" \"*.vtt\"\n # Metadata\n \"*.nfo\" \"*.tbn\"\n # Images\n \"*.jpg\" \"*.jpeg\" \"*.png\" \"*.webp\" \"*.svg\"\n \"poster.*\" \"fanart.*\" \"backdrop.*\" \"clearlogo.*\"\n \"banner.*\" \"thumb.*\" \"landscape.*\"\n # Kodi/Emby extras — not tracked by Radarr API\n \"*-trailer.*\" \"*-featurette.*\" \"*-behindthescenes.*\"\n \"*-interview.*\" \"*-scene.*\" \"*-short.*\" \"*-deleted.*\"\n \"*-clip.*\" \"*-other.*\"\n # Theme songs — stored in movie folder, not tracked by arr\n \"theme.mp3\" \"theme.flac\" \"theme.wav\" \"theme.m4a\" \"theme.mka\"\n )\n\n# Radarr/Sonarr TMDb/TVDB removed entry cleanup\n RADARR_DROPPED_ADD_EXCLUSION=true # add removed movies to import exclusion list\n SONARR_DROPPED_ADD_EXCLUSION=true # add removed series to import exclusion list\n\n# ━━━ Arr Failed/Stalled Recovery ━━━\n# Auto blocklist + re-search failed imports and stalled downloads.\n# Runs every 6 hours — schedule: 0 */6 * * *\n#\n# Targets four problem types:\n# importFailed — downloaded but arr couldn't import\n# importPending — downloaded, stuck waiting to import (won't self-resolve)\n# error status — serious failure not covered above\n# stalled — download stuck with no connections or progress\n#\n# Items newer than ARR_IMPORT_RECOVERY_AGE are skipped — gives arr time to retry first.\n# Per-host recovery toggles (HOST1_SONARR_RECOVERY etc.) live in host*.conf.\n ARR_IMPORT_RECOVERY_AGE=6 # hours — skip items newer than this\n # matches cron interval — items eligible after one missed cycle\n\n# ==============================================================================================\n# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Session-based storage allocator using filesystem symlink indirection.\n# ffmpeg resolves the symlink ONCE at session start — existing sessions never affected.\n# Symlink flips between ramdisk and SSD are transparent to active streams.\n#\n# ⚠️ Docker mount — must use shared propagation so symlink flips work inside container:\n# --mount type=bind,source=/mnt/ram-transcode,target=/ext-ram-transcode,bind-propagation=shared\n# Standard path mappings use rprivate — symlink changes are NOT visible inside container.\n\n# ━━━ Transcode Manager ━━━\n# tmpfs mount point — created at array start by ramdisk_setup.sh.\n# Must exist before Emby starts so the symlink resolves correctly.\n RAMDISK_PATH=\"/mnt/ramdisk_transcodes\"\n\n# Ramdisk size and flip thresholds — defined per host in host*.conf.\n# All three are coupled — if size changes, thresholds must change with it.\n# HOST1_RAMDISK_SIZE / HOST2_RAMDISK_SIZE\n# HOST1_RAMDISK_WARN_GB / HOST2_RAMDISK_WARN_GB ← flip to SSD at this usage\n# HOST1_RAMDISK_LOW_GB / HOST2_RAMDISK_LOW_GB ← flip back to ramdisk at this usage\n\n# Symlink that Emby points at — this path NEVER changes regardless of ramdisk/SSD state.\n# Emby resolves the symlink once per session at start — symlink flips are transparent.\n# Must match the container path configured in Emby's Extra Parameters.\n TRANSCODE_LINK=\"/mnt/ram-transcode\"\n\n# SSD fallback location — defined per host in host*.conf (cache path differs per server):\n# HOST1_TRANSCODE_SSD / HOST2_TRANSCODE_SSD\n\n# Minimum free GB on SSD before allowing flip from ramdisk to SSD.\n RAMDISK_SSD_MIN_GB=20\n\n TRANSCODE_MAX_AGE=20 # minutes — HLS segment age before cleanup eligibility\n TRANSCODE_ORPHAN_AGE=30 # minutes — files with no matching active session\n TRANSCODE_FLIP_WARN=3 # notify if symlink flips this many times in one hour\n\n TRANSCODE_OWNER=\"nobody:users\"\n TRANSCODE_CHMOD=\"755\"\n\n# Operating mode — controls symlink direction behaviour.\n# smart — auto-flips between ramdisk and SSD based on thresholds (default)\n# ramdisk — always uses ramdisk, warns if RAMDISK_WARN_GB exceeded but holds\n# ssd — always uses SSD, never flips to ramdisk\n TRANSCODE_MANAGER_MODE=\"smart\"\n\n# Daily statistics log — read by weekly_health_digest.sh for transcode summary.\n TRANSCODE_STATE_FILE=\"$STATE_DIR/transcode_state.db\"\n TRANSCODE_DAILY_LOG=\"$DATA_DIR/transcode_daily.db\"\n TRANSCODE_LOG_RETENTION=90 # days before old entries purged\n\n TRANSCODE_CHECK_EMBY=true\n\n# ━━━ Transcode Server Array ━━━\n# All media servers sharing the ramdisk transcode space.\n# Format: \"ContainerName|URL|APIKey|Type\" — Type: emby | jellyfin | plex\n# Entries with placeholder API keys are skipped automatically.\n# ⚠️ Tdarr does NOT belong here — keep Tdarr on SSD, not ramdisk.\n# Defined per host in host*.conf — Emby container names and keys differ per server:\n# HOST1_TRANSCODE_SERVERS / HOST2_TRANSCODE_SERVERS\n\n# ==============================================================================================\n# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n\n# ━━━ Certificate Monitor ━━━\n# Checks SSL certificate expiry via direct openssl connection — no NPM dependency.\n# Checks the actual certificate served by each domain, not what NPM thinks it has.\n# Domains defined per host in host*.conf — each server monitors its own domains:\n# HOST1_CERT_MONITOR_DOMAINS / HOST2_CERT_MONITOR_DOMAINS\n CERT_WARN_DAYS=30 # warn when cert expires within this many days\n CERT_CRIT_DAYS=7 # critical alert within this many days\n CERT_TIMEOUT=10 # seconds per domain before giving up\n\n# ━━━ Backup Verify ━━━\n# Verifies rsync mirror health by comparing random file checksums between servers.\n# Catches silent corruption or incomplete syncs that rsync itself wouldn't detect.\n# Defined per host in host*.conf — leave empty to use HOST*_DAILY_SYNC_SHARES automatically:\n# HOST1_BACKUP_VERIFY_SHARES / HOST2_BACKUP_VERIFY_SHARES\n BACKUP_VERIFY_SAMPLE=10 # random files to check per share\n BACKUP_VERIFY_MIN_SIZE=1M # minimum file size to include in sample\n\n# ━━━ SMART Health ━━━\n# Monitors drive SMART attributes — discovers all drives via /dev/sd* and /dev/nvme*.\n# Thresholds read from /boot/config/plugins/dynamix/dynamix.cfg at runtime\n# (hot/max/hotssd/maxssd) — these vars are fallback only if dynamix.cfg not found.\n SMART_TEMP_WARN=45 # fallback — Celsius warn threshold\n SMART_TEMP_CRIT=55 # fallback — Celsius critical threshold\n# Drives to ignore defined per host in host*.conf — hardware is server-specific:\n# HOST1_SMART_IGNORE_DRIVES / HOST2_SMART_IGNORE_DRIVES\n\n# ━━━ ZFS Memory Snapshot ━━━\n# Weekly ZFS pool health and memory diagnostic report — informational only.\n ZFS_REPORT_LOG=\"$DATA_DIR/zfs-weekly-health.log\"\n ZFS_REPORT_ARC_WARN_PCT=90 # warn if ARC using more than this % of its max\n ZFS_REPORT_FREE_WARN_GB=10 # warn if less than this GB free RAM\n ZFS_REPORT_AVAIL_WARN_GB=20 # warn if less than this GB available on ZFS pool\n ZFS_REPORT_DOCKER_TOP=10 # how many top Docker containers to show by memory\n# Pool ignore list defined per host in host*.conf — pool names are server-specific:\n# HOST1_ZFS_REPORT_IGNORE_POOLS / HOST2_ZFS_REPORT_IGNORE_POOLS\n\n# ━━━ Bandwidth Monitor ━━━\n# Called automatically by rsync.sh after each sync — one bounded write per run.\n# Tracks transfer size, duration and profile per sync for weekly summary reporting.\n BANDWIDTH_LOG=\"$DATA_DIR/bandwidth_history.db\"\n BANDWIDTH_LOG_RETENTION=90 # days before old entries purged\n BANDWIDTH_WARN_GB=50 # flag syncs larger than this in weekly report\n\n# Stats files — written by cleanup and recovery scripts, read by coffee report.\n# All in DATA_DIR — array always running when these are written.\n ARR_CLEANUP_STATS=\"$DATA_DIR/arr_cleanup_stats.db\" # lidarr/sonarr/radarr orphan stats\n ARR_RECOVERY_STATS=\"$DATA_DIR/arr_recovery_stats.db\" # blocklist + re-search stats\n\n# ━━━ Health Digest ━━━\n# Aggregated system health summary — reads existing state files, no new writes.\n# Three profiles control when the digest is sent:\n# always — sends every run regardless of findings\n# smart — sends only when DIGEST_SMART_ON_* conditions are found\n# weekly — sends once per week on DIGEST_DAY only\n DIGEST_PROFILE=\"weekly\" # always | smart | weekly\n DIGEST_DAY=\"Sunday\"\n DIGEST_SMART_ON_WATCHDOG=true # send if any watchdog strikes are active\n DIGEST_SMART_ON_FALLBACK=true # send if fallback state is not NORMAL\n DIGEST_SMART_ON_CERT_WARN=true # send if any cert is under CERT_WARN_DAYS\n DIGEST_SMART_ON_BANDWIDTH=true # send if any transfer exceeded BANDWIDTH_WARN_GB\n\n# ━━━ Emby Session Report ━━━\n# Weekly Emby usage statistics via API — no persistent writes, queries fresh each run.\n# URL and API key pulled from HOST*_EMBY_URL and HOST*_EMBY_API_KEY in host*.conf.\n EMBY_REPORT_DAYS=7 # days to include in the report period\n EMBY_REPORT_TOP_N=10 # number of top content items to show\n\n# ==============================================================================================\n# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Pressure reduction layer — keeps the system comfortable before things break.\n# Called by watchdog_orchestrator.sh. Single-pass, not a continuous loop.\n#\n# ── PRESSURE LEVELS ───────────────────────────────────────────────────────────────────────────\n# Level 1 (soft) — throttle SABnzbd + qBit download speeds\n# Level 2 (medium) — further throttle + docker pause background containers\n# Level 3 (hard) — docker stop optional containers, signal docker_watchdog to defer\n#\n# ── PER-HOST CONTAINER LISTS ──────────────────────────────────────────────────────────────────\n# HOST*_RW_PAUSE_CONTAINERS — docker pause at medium pressure (in host*.conf)\n# HOST*_RW_STOP_CONTAINERS — docker stop at hard pressure (in host*.conf)\n\n RW_ENABLED=true\n RW_STATE_FILE=\"$STATE_DIR/resource_watchdog_state.db\"\n\n# ━━━ Pressure Thresholds ━━━\n# Graduated RAM response — resource_watchdog acts before system_watchdog reboots.\n# RW_RAM_SOFT_GB > RW_RAM_MEDIUM_GB > RW_RAM_HARD_GB > SYS_WATCHDOG_MEM_GB always\n RW_RAM_SOFT_GB=12 # throttle start — reduce background load\n RW_RAM_MEDIUM_GB=8 # pause background containers\n RW_RAM_HARD_GB=6 # stop optional containers (was SYS_WATCHDOG_MEM_SHUTDOWN_GB)\n RW_RAM_RECOVER_GB=20 # RAM must reach this before restoring hard-stopped containers\n\n# Load average thresholds — multiplier × core count\n RW_LOAD_SOFT_MULTIPLIER=2.0 # soft pressure: 2× cores sustained\n RW_LOAD_MEDIUM_MULTIPLIER=3.0 # medium pressure: 3× cores sustained\n\n# Consecutive runs at lower pressure before de-escalating\n RW_RECOVER_CYCLES=3\n\n# ━━━ SABnzbd Throttle ━━━\n# Speed values: \"50M\" = 50 MB/s, \"0\" = unlimited\n RW_SABNZBD_ENABLED=true\n RW_SABNZBD_SPEED_SOFT=\"50M\"\n RW_SABNZBD_SPEED_MEDIUM=\"10M\"\n\n# ━━━ qBittorrent Throttle ━━━\n# KB/s — 0 = unlimited\n RW_QBIT_ENABLED=true\n RW_QBIT_DL_SOFT=51200 # 50 MB/s\n RW_QBIT_DL_MEDIUM=10240 # 10 MB/s\n\n# ━━━ Critical Containers ━━━\n# Never paused or stopped regardless of pressure level.\n# Keep DNS, auth, media serving, and live TV always running.\n RW_CRITICAL_CONTAINERS=(\n \"NginxProxyManager\" # reverse proxy — internet access\n \"Authelia\" # auth — nothing accessible without it\n \"Authelia-Secondary\"\n \"Mariadb-Authelia\" # Authelia dependency\n \"Mariadb-Authelia-Secondary\"\n \"Redis-Authelia\" # Authelia dependency\n \"Redis-Authelia-Secondary\"\n \"AdGuard-Home\" # DNS — all LAN resolution\n \"Emby\" # media server — Live TV buffering\n \"Dispatcharr\" # Live TV scheduler — loses state if stopped\n \"Dispatcharr-Basic\"\n \"Dispatcharr-Iptv-Users\"\n )\n\n# ==============================================================================================\n# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────\n# ==============================================================================================\n# Single-pass system health check — last line of defense before a crash.\n# Called by watchdog_orchestrator.sh every minute. NOT started by array_started.sh.\n# Re-sources config at each orchestrator run — config changes take effect immediately.\n#\n# ── THREE-TIER RESPONSE SYSTEM ────────────────────────────────────────────────────────────────\n# CRITICAL — bypass ALL strikes, reboot immediately\n# Docker daemon down, rootfs 100%, kernel oops, FD exhaustion, /boot read-only\n#\n# URGENT — bypass strikes only when OOM confirms active crisis\n# RAM < MEM_GB AND OOM kills >= OOM_LIMIT in this cycle → reboot NOW\n# Without OOM confirmation → normal strike system\n#\n# STANDARD — strike system (N consecutive failures → reboot)\n# RAM tiers, high load, CPU temp, zombies, /var/log, /tmp, containers\n#\n# ── RAM ───────────────────────────────────────────────────────────────────────────────────────\n# SYS_WATCHDOG_MEM_GB — strike system → reboot (or OOM bypass)\n# Warn/shutdown/recover RAM tiers are handled by resource_watchdog.sh\n\n# ━━━ State Files ━━━\n SYS_WATCHDOG_STATE_FILE=\"$STATE_DIR/system_watchdog_state.db\"\n DOCKER_WATCHDOG_FAILED_FILE=\"$STATE_DIR/docker_watchdog_failed.db\"\n SYS_WATCHDOG_REBOOT_LOG=\"$STATE_DIR/system_watchdog_reboots.db\"\n SYS_WATCHDOG_OOM_FILE=\"$STATE_DIR/system_watchdog_oom.db\"\n\n# ━━━ Strike and Reboot Loop Settings ━━━\n# Strike system: a check must fail this many consecutive cycles before action is taken.\n# Single spikes (one bad reading) are ignored — sustained problems trigger reboot.\n SYS_WATCHDOG_STRIKE_LIMIT=2 # consecutive failures before reboot trigger\n\n# Reboot loop protection — if system keeps rebooting something is seriously wrong.\n# After REBOOT_LIMIT reboots in REBOOT_WINDOW_HRS → shutdown instead of reboot.\n SYS_WATCHDOG_REBOOT_LIMIT=3\n SYS_WATCHDOG_REBOOT_WINDOW_HRS=12\n\n# ━━━ RAM Reboot Threshold ━━━\n# Reboot trigger only — warn/shutdown/recover handled by resource_watchdog.sh\n# RW_RAM_HARD_GB > SYS_WATCHDOG_MEM_GB always (RM acts before watchdog reboots)\n SYS_WATCHDOG_MEM_GB=4 # strike system → reboot\n\n# ━━━ OOM Bypass Settings ━━━\n# OOM bypass: if RAM is critically low AND kernel OOM kills exceed this threshold\n# in a single cycle → bypass strike system and reboot immediately.\n# Rate-based: kills per 5-minute cycle, not absolute count.\n# Rationale: 1-2 kills = docker_watchdog handles it ✅\n# 3+ kills while RAM critical = system dying faster than watchdogs can heal ✅\n SYS_WATCHDOG_OOM_LIMIT=3 # OOM kills in one cycle to trigger bypass\n\n# ━━━ Thresholds ━━━\n# Set at \"about to become unstable\" — not \"things are a bit high\".\n\n# rootfs (/) usage — two levels: strike at 95%, critical bypass at 99%\n SYS_WATCHDOG_ROOTFS_PCT=95 # standard strike threshold\n SYS_WATCHDOG_ROOTFS_CRITICAL_PCT=99 # bypass strikes — truly full, writes failing\n\n# /var/log usage — log spam indicates something broken\n SYS_WATCHDOG_LOG_PCT=95\n\n# /tmp usage — tmpfs fills from downloads, lock files fail at 100%\n SYS_WATCHDOG_TMP_PCT=90 # warn + attempt clear\n SYS_WATCHDOG_TMP_CRITICAL_PCT=98 # bypass strikes if clear failed\n\n# ZFS ARC pinned percentage — ARC not releasing after reclaim = memory stuck\n SYS_WATCHDOG_ARC_PINNED_PCT=98\n SYS_WATCHDOG_ARC_RELEASE_PCT=95\n\n# Load average multiplier — threshold = MULTIPLIER × CPU core count\n# MULTIPLIER=3 on 16-core = load of 48 before triggering\n SYS_WATCHDOG_LOAD_MULTIPLIER=3\n\n# Zombie process count — large numbers = serious process management failure\n SYS_WATCHDOG_ZOMBIE_LIMIT=50\n\n# CPU temperature — sustained high temp causes throttling or kernel panic\n SYS_WATCHDOG_CPU_TEMP_MAX=95\n\n# File descriptor exhaustion — system-wide FD limit near exhaustion\n# New connections fail silently, Docker can't spawn processes, SSH fails\n SYS_WATCHDOG_FD_CRITICAL_PCT=95 # bypass strikes — critical tier\n\n# Runaway process — single non-container process consuming excessive CPU\n# Multiple strikes before action — single spikes are normal\n SYS_WATCHDOG_RUNAWAY_CPU_PCT=90 # % single process must sustain\n SYS_WATCHDOG_RUNAWAY_STRIKES=3 # consecutive cycles before warning\n\n# Array disk errors — accumulating mdstat errors = disk failing NOW\n SYS_WATCHDOG_MDSTAT_ERROR_LIMIT=5 # new errors in one cycle before acting\n\n# ━━━ Check Toggles ━━━\n# Per-host — moved to host*.conf\n# Different servers may have different hardware, NICs, and check requirements\n# See HOST*_SYS_WATCHDOG_CHECK_* in host*.conf\n\n# ━━━ Abort Toggles ━━━\n# Conditions that prevent reboot even when a threshold is hit.\n# CRITICAL tier bypasses these — a truly critical condition reboots regardless.\n# true = abort standard reboot if this condition is active\n# false = reboot anyway\n SYS_WATCHDOG_ABORT_ON_ZFS_UNHEALTHY=true # rebooting with bad pool risks data loss\n SYS_WATCHDOG_ABORT_ON_PARITY=false # aborting parity better than crashing mid-check\n SYS_WATCHDOG_ABORT_ON_MOVER=false # aborting move better than crashing mid-move\n\n# ==============================================================================================\n# ──────────────────────── End Of User Variables ───────────────────────────────────────────────\n# ==============================================================================================\n","numLines":1439,"startLine":1,"totalLines":1439}},"sourceToolAssistantUUID":"aaac3e1e-059c-4eb8-8fa9-93febe5db3b4","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"last-prompt","lastPrompt":"yes, do that. and lets make sure our claud script loads it on an unraid reboot","leafUuid":"27b1ca71-24ca-4b96-991d-a2dbaa5fc50e","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"ai-title","aiTitle":"Troubleshoot NPM authentication failure","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"mode","mode":"normal","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"permission-mode","permissionMode":"default","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"parentUuid":"27b1ca71-24ca-4b96-991d-a2dbaa5fc50e","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_016ApKdH5bEKDFbp99byPzEp","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"Et8CCmUIDhgCKkABXAhOElX9oMQxNM/g0bj2O0QbSRuvDEaTHg2V/aZ5wVuUbDuj9P6d27Tz0mZv7a0NwbVtufabE8qe5NzWAULxMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIMIV0mNQUW7hrmEUpFGgwWiqVwCNkBKx9fAO4iMBNHIqhVSyaR7TJhzQpUlMkt0c2E8cEC+xFHpc2KSwMS5AgB12FrwvX3blJsAy1QviqnAbPj1qe8piL9CmlZAfyhDGvf+pg09MTMz/4w62/z1OSScSoGMq1AZ0MZBTy+5RLidwE4dU7NCUmueBn5NazFuBVqMz+aNfLdVzcP+j+LZ6pZIoi73ZfciOpcpDHEFq54K9tSxGju1wAAEk2zUMao1f3yDk1ZSMXmtX6whRHBVpbzU8lvorx8d5y4zJ5hBAkg5seJcyZGa4dwpz20QQweEJ267hlombK2GAE="}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":29043,"cache_read_input_tokens":55630,"output_tokens":246,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29043,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":246,"cache_read_input_tokens":55630,"cache_creation_input_tokens":29043,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29043},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6cKjH3TKLJ5jaUhEC","type":"assistant","uuid":"c42df0c9-eb59-4d97-b831-0ea9b92a9f63","timestamp":"2026-06-06T17:53:28.684Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"c42df0c9-eb59-4d97-b831-0ea9b92a9f63","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_016ApKdH5bEKDFbp99byPzEp","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me check the remaining memory files and the array_started.sh to understand the boot sequence."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":29043,"cache_read_input_tokens":55630,"output_tokens":246,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29043,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":246,"cache_read_input_tokens":55630,"cache_creation_input_tokens":29043,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29043},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6cKjH3TKLJ5jaUhEC","type":"assistant","uuid":"013e7b4e-1634-4e21-bb1e-3d5e9c86aa05","timestamp":"2026-06-06T17:53:29.563Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"013e7b4e-1634-4e21-bb1e-3d5e9c86aa05","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_016ApKdH5bEKDFbp99byPzEp","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01UPuC4uZnCMBD2ZMuwhvcXv","name":"Read","input":{"file_path":"/root/.claude/projects/-root/memory/feedback_commits.md"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":29043,"cache_read_input_tokens":55630,"output_tokens":246,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29043,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":246,"cache_read_input_tokens":55630,"cache_creation_input_tokens":29043,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29043},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6cKjH3TKLJ5jaUhEC","type":"assistant","uuid":"d3ce9115-6bee-4a0d-9b91-6b8f2f97e0b5","timestamp":"2026-06-06T17:53:30.014Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"d3ce9115-6bee-4a0d-9b91-6b8f2f97e0b5","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01UPuC4uZnCMBD2ZMuwhvcXv","type":"tool_result","content":"1\t---\n2\tname: feedback_commits\n3\tdescription: Never add Co-Authored-By to commits unless user explicitly asks\n4\tmetadata: \n5\t node_type: memory\n6\t type: feedback\n7\t originSessionId: a1ac522e-9103-4757-b6d0-a7cd2f5681fb\n8\t---\n9\t\n10\tDo not add \"Co-Authored-By: Claude...\" to commit messages.\n11\t\n12\t**Why:** User preference — they didn't ask for it.\n13\t**How to apply:** Write plain commit messages with no co-author trailer unless the user explicitly says to include it.\n14\t"}]},"uuid":"008103e0-409f-42b8-b1ed-4d7ff7927397","timestamp":"2026-06-06T17:53:30.026Z","toolUseResult":{"type":"text","file":{"filePath":"/root/.claude/projects/-root/memory/feedback_commits.md","content":"---\nname: feedback_commits\ndescription: Never add Co-Authored-By to commits unless user explicitly asks\nmetadata: \n node_type: memory\n type: feedback\n originSessionId: a1ac522e-9103-4757-b6d0-a7cd2f5681fb\n---\n\nDo not add \"Co-Authored-By: Claude...\" to commit messages.\n\n**Why:** User preference — they didn't ask for it.\n**How to apply:** Write plain commit messages with no co-author trailer unless the user explicitly says to include it.\n","numLines":14,"startLine":1,"totalLines":14}},"sourceToolAssistantUUID":"d3ce9115-6bee-4a0d-9b91-6b8f2f97e0b5","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"008103e0-409f-42b8-b1ed-4d7ff7927397","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_016ApKdH5bEKDFbp99byPzEp","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01HeWuuSrd7yaf1aiY5mmbue","name":"Read","input":{"file_path":"/root/.claude/projects/-root/memory/feedback_dev_vs_prod.md"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":29043,"cache_read_input_tokens":55630,"output_tokens":246,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29043,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":246,"cache_read_input_tokens":55630,"cache_creation_input_tokens":29043,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29043},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6cKjH3TKLJ5jaUhEC","type":"assistant","uuid":"69288e36-52d2-4f1a-9ef7-474fba4e6b8f","timestamp":"2026-06-06T17:53:30.461Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"69288e36-52d2-4f1a-9ef7-474fba4e6b8f","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01HeWuuSrd7yaf1aiY5mmbue","type":"tool_result","content":"1\t---\n2\tname: feedback_dev_vs_prod\n3\tdescription: User had a bad week where dev and production folders got mixed up — always confirm we are working in the single authoritative workspace\n4\tmetadata: \n5\t node_type: memory\n6\t type: feedback\n7\t originSessionId: d6d51683-5f05-41b4-ad5d-afbe06985ad9\n8\t---\n9\t\n10\tAlways work exclusively in `/boot/config/plugins/varaverk` (the workspace). Never touch `/mnt/user/Important Shit/Git/Development/Varaverk` — that is a stale dev folder.\n11\t\n12\t**Why:** User spent a week dealing with confusion between dev folder, installed dir, and workspace all drifting out of sync. This caused significant lost work and frustration. The `.plg` now symlinks the installed location directly to the workspace, so there is only one copy.\n13\t\n14\t**How to apply:** If asked to edit any Varaverk file, always use the path under `/boot/config/plugins/varaverk/`. If the dev folder ever comes up, flag that it is stale and should be ignored.\n15\t"}]},"uuid":"9a38aef8-3819-42c4-a85b-e004cbfc71c2","timestamp":"2026-06-06T17:53:30.476Z","toolUseResult":{"type":"text","file":{"filePath":"/root/.claude/projects/-root/memory/feedback_dev_vs_prod.md","content":"---\nname: feedback_dev_vs_prod\ndescription: User had a bad week where dev and production folders got mixed up — always confirm we are working in the single authoritative workspace\nmetadata: \n node_type: memory\n type: feedback\n originSessionId: d6d51683-5f05-41b4-ad5d-afbe06985ad9\n---\n\nAlways work exclusively in `/boot/config/plugins/varaverk` (the workspace). Never touch `/mnt/user/Important Shit/Git/Development/Varaverk` — that is a stale dev folder.\n\n**Why:** User spent a week dealing with confusion between dev folder, installed dir, and workspace all drifting out of sync. This caused significant lost work and frustration. The `.plg` now symlinks the installed location directly to the workspace, so there is only one copy.\n\n**How to apply:** If asked to edit any Varaverk file, always use the path under `/boot/config/plugins/varaverk/`. If the dev folder ever comes up, flag that it is stale and should be ignored.\n","numLines":15,"startLine":1,"totalLines":15}},"sourceToolAssistantUUID":"69288e36-52d2-4f1a-9ef7-474fba4e6b8f","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"9a38aef8-3819-42c4-a85b-e004cbfc71c2","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_016ApKdH5bEKDFbp99byPzEp","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01ShsRS5Aiuu5d45dKnxHD12","name":"Read","input":{"file_path":"/boot/config/plugins/varaverk/Orchestrators/array_started.sh"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":29043,"cache_read_input_tokens":55630,"output_tokens":246,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29043,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":246,"cache_read_input_tokens":55630,"cache_creation_input_tokens":29043,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29043},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS6cKjH3TKLJ5jaUhEC","type":"assistant","uuid":"b327df86-1769-4004-8e13-94a48b5b5036","timestamp":"2026-06-06T17:53:31.264Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"b327df86-1769-4004-8e13-94a48b5b5036","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01ShsRS5Aiuu5d45dKnxHD12","type":"tool_result","content":"1\t#!/bin/bash\n2\t# ==============================================================================================\n3\t# ================================= Array Start Orchestrator ===================================\n4\t# ==============================================================================================\n5\t# Single entry point for array start — fired by the Varaverk plugin's\n6\t# disks_mounted event hook (Plugin/unraid/event/disks_mounted/array_start_jobs).\n7\t# Launches everything configured in ARRAY_START_SCRIPTS in master.conf.\n8\t# This script exits after launching all scripts — the event hook sees it complete normally.\n9\t#\n10\t# ── WHAT IT LAUNCHES ──────────────────────────────────────────────────────────────────────────\n11\t# Configured in master.conf ARRAY_START_SCRIPTS — no changes to this script ever needed.\n12\t# Current order (order matters — see below):\n13\t#\n14\t# ONE-SHOT (run and exit naturally):\n15\t# System_Essentials/unraid_api_key_renew.sh — re-register Varaverk API key at boot\n16\t# System_Essentials/inotify_tuning.sh — raise inotify limits before containers start\n17\t# System_Essentials/docker_syslog_filter.sh — suppress veth log noise before logs fill\n18\t# System_Essentials/php_fpm_max_children.sh — WebGUI performance tuning\n19\t# Transcodes/ramdisk_setup.sh — create tmpfs + symlink before Emby starts\n20\t# Docker_Essentials/docker_network_connect.sh — ensure networks + container connections\n21\t#\n22\t# CONTINUOUS (run until array stops):\n23\t# Fallback/fallback.sh — mutual fallback monitor\n24\t#\n25\t# NOTE: watchdogs (docker, system, stability) are NOT launched here.\n26\t# They run via watchdog_orchestrator.sh every 15 min (cron), not as daemons.\n27\t#\n28\t# ── WHY ORDER MATTERS ─────────────────────────────────────────────────────────────────────────\n29\t# unraid_api_key_renew.sh — before anything else — self-heals API registry at boot\n30\t# inotify_tuning.sh — must run BEFORE Code-Server and other containers start\n31\t# containers that start with low inotify limits keep them ✅\n32\t# docker_syslog_filter — must run BEFORE any container starts creating veth interfaces\n33\t# ramdisk_setup.sh — must run BEFORE Emby starts transcoding\n34\t# docker_network_connect — must run BEFORE watchdogs check container states\n35\t# fallback.sh — last — needs everything else stable to make decisions\n36\t#\n37\t# ── ONE-SHOT vs CONTINUOUS DETECTION ─────────────────────────────────────────────────────────\n38\t# Script is launched in background with bash script.sh &\n39\t# After 1 second: if PID still alive → continuous (running in background)\n40\t# if PID dead + exit 0 → one-shot completed successfully\n41\t# if PID dead + exit N → failure\n42\t#\n43\t# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────\n44\t# Root check — all launched scripts require root\n45\t# acquire_lock — prevents duplicate array start launches\n46\t# detect_hosts() — MY_ID in notifications\n47\t# platform_require_cmd — notify validated before use\n48\t# chmod +x auto-fix — non-executable scripts fixed before launch\n49\t# Full path on failure — shows exact path for debugging\n50\t# notify on failures — alert if any script fails to launch\n51\t#\n52\t# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────\n53\t# ARRAY_START_SCRIPTS — ordered list of scripts to launch at array start\n54\t#\n55\t# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────\n56\t# array_started.sh — normal launch (called by Varaverk disks_mounted event hook)\n57\t# array_started.sh --dry-run — show what would be launched without launching\n58\t# array_started.sh --status — show configured scripts and their current state\n59\t# array_started.sh --log — verbose output per script\n60\t# ==============================================================================================\n61\t\n62\tSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n63\tECOSYSTEM_ROOT=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\n64\t\n65\tsource \"$ECOSYSTEM_ROOT/load_config.sh\"\n66\t\n67\tparse_args \"$@\"\n68\t\n69\t# ==============================================================================================\n70\t# ━━━ Setup ━━━\n71\t# ==============================================================================================\n72\tif [[ \"$EUID\" -ne 0 ]]; then\n73\t error \"Must be run as root\"\n74\t exit 1\n75\tfi\n76\t\n77\tplatform_require_cmd \\\n78\t \"/usr/local/emhttp/plugins/dynamix/scripts/notify\" \\\n79\t \"\" \"\" \\\n80\t \"unRAID notify script\" || warn \"unRAID notify script not found — native notifications disabled\"\n81\t\n82\tacquire_lock\n83\t\n84\tdetect_hosts\n85\t\n86\t[[ \"$DRY_RUN\" == true ]] && warn \"DRY RUN — scripts will not be launched\"\n87\t\n88\t# ==============================================================================================\n89\t# ━━━ Status ━━━\n90\t# ==============================================================================================\n91\tif [[ \"$SHOW_STATUS\" == true ]]; then\n92\t echo \"\"\n93\t echo \"━━━━━ $ICON_SUMMARY ARRAY START STATUS ━━━━━\"\n94\t echo \"$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)\"\n95\t echo \"$ICON_GEAR Scripts: ${#ARRAY_START_SCRIPTS[@]} configured\"\n96\t echo \"\"\n97\t\n98\t for relative_path in \"${ARRAY_START_SCRIPTS[@]}\"; do\n99\t [[ -z \"$relative_path\" ]] && continue\n100\t script_path=\"$ECOSYSTEM_ROOT/$relative_path\"\n101\t script_name=$(basename \"$script_path\")\n102\t\n103\t if [[ ! -f \"$script_path\" ]]; then\n104\t echo \" $ICON_ERROR $script_name — FILE NOT FOUND\"\n105\t echo \" $script_path\"\n106\t continue\n107\t fi\n108\t\n109\t [[ ! -x \"$script_path\" ]] && flag=\" (not executable — will auto-fix)\" || flag=\"\"\n110\t\n111\t # Check if currently running\n112\t if pgrep -f \"$script_path\" >/dev/null 2>&1; then\n113\t RUN_PID=$(pgrep -f \"$script_path\" | head -1)\n114\t echo \" $ICON_RUNNING $script_name — RUNNING (PID $RUN_PID)${flag}\"\n115\t else\n116\t echo \" $ICON_NOT_RUNNING $script_name — not running${flag}\"\n117\t fi\n118\t done\n119\t echo \"━━━━━━━━━━━━━━━━━━━━━━━\"\n120\t exit 0\n121\tfi\n122\t\n123\t# ==============================================================================================\n124\t# ━━━ Launch Scripts ━━━\n125\t# ==============================================================================================\n126\tif [[ ${#ARRAY_START_SCRIPTS[@]} -eq 0 ]]; then\n127\t notify \"Array started on $LOCAL_SERVER_NAME ($MY_ID) but ARRAY_START_SCRIPTS is empty — boot sequence skipped. Check master.conf.\" \\\n128\t \"Array Start\" \"alert\"\n129\t error \"ARRAY_START_SCRIPTS is empty — check master.conf\"\n130\t exit 1\n131\tfi\n132\t\n133\techo \"\"\n134\techo \"━━━ $ICON_GEAR Array Start — $MY_ID — $(date '+%Y-%m-%d %H:%M:%S') ━━━\"\n135\tlog \"Ecosystem root: $ECOSYSTEM_ROOT\"\n136\tlog \"Launching ${#ARRAY_START_SCRIPTS[@]} script(s)...\"\n137\techo \"\"\n138\t\n139\tSTART=$(date +%s)\n140\tLAUNCHED=0\n141\tFAILED=0\n142\tFAILED_SCRIPTS=()\n143\t\n144\tfor relative_path in \"${ARRAY_START_SCRIPTS[@]}\"; do\n145\t [[ -z \"$relative_path\" ]] && continue\n146\t\n147\t SCRIPT_PATH=\"$ECOSYSTEM_ROOT/$relative_path\"\n148\t SCRIPT_NAME=$(basename \"$SCRIPT_PATH\")\n149\t\n150\t # File existence check\n151\t if [[ ! -f \"$SCRIPT_PATH\" ]]; then\n152\t error \"$SCRIPT_NAME — not found\"\n153\t error \" Expected: $SCRIPT_PATH\"\n154\t (( FAILED++ ))\n155\t FAILED_SCRIPTS+=(\"$SCRIPT_NAME\")\n156\t continue\n157\t fi\n158\t\n159\t # Auto-fix permissions — chmod +x if needed\n160\t if [[ ! -x \"$SCRIPT_PATH\" ]]; then\n161\t warn \"$SCRIPT_NAME — not executable, fixing...\"\n162\t chmod +x \"$SCRIPT_PATH\" || {\n163\t error \"$SCRIPT_NAME — chmod +x failed\"\n164\t (( FAILED++ ))\n165\t FAILED_SCRIPTS+=(\"$SCRIPT_NAME\")\n166\t continue\n167\t }\n168\t fi\n169\t\n170\t if [[ \"$DRY_RUN\" == true ]]; then\n171\t warn \"DRY RUN — would launch: $SCRIPT_NAME\"\n172\t (( LAUNCHED++ ))\n173\t continue\n174\t fi\n175\t\n176\t log \"$ICON_START Launching $SCRIPT_NAME...\"\n177\t bash \"$SCRIPT_PATH\" &\n178\t PID=$!\n179\t\n180\t # Brief settle — 1s enough to detect immediate failures\n181\t sleep 1\n182\t\n183\t if kill -0 \"$PID\" 2>/dev/null; then\n184\t # Still running → continuous script\n185\t warn \"$SCRIPT_NAME — running (PID $PID) ✅\"\n186\t (( LAUNCHED++ ))\n187\t else\n188\t # Exited — check if one-shot success or failure\n189\t wait \"$PID\"\n190\t EXIT_CODE=$?\n191\t if [[ \"$EXIT_CODE\" -eq 0 ]]; then\n192\t log \"$SCRIPT_NAME — completed (one-shot) ✅\"\n193\t (( LAUNCHED++ ))\n194\t else\n195\t error \"$SCRIPT_NAME — exited with code $EXIT_CODE\"\n196\t error \" Path: $SCRIPT_PATH\"\n197\t (( FAILED++ ))\n198\t FAILED_SCRIPTS+=(\"$SCRIPT_NAME\")\n199\t fi\n200\t fi\n201\t\n202\tdone\n203\t\n204\tEND=$(date +%s)\n205\t\n206\t# ==============================================================================================\n207\t# ━━━ Summary ━━━\n208\t# ==============================================================================================\n209\techo \"\"\n210\techo \"━━━━━ $ICON_SUMMARY ARRAY START SUMMARY ━━━━━\"\n211\techo \"$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)\"\n212\techo \"$ICON_SUCCESS Launched: $LAUNCHED\"\n213\t[[ \"$FAILED\" -gt 0 ]] && echo \"$ICON_ERROR Failed: $FAILED — ${FAILED_SCRIPTS[*]}\"\n214\techo \"$ICON_TIME Duration: $(format_duration $(( END - START )))\"\n215\techo \"\"\n216\t\n217\tif [[ \"$DRY_RUN\" == true ]]; then\n218\t warn \"DRY RUN — no scripts launched\"\n219\telif [[ \"$FAILED\" -gt 0 ]]; then\n220\t warn \"Status: $FAILED script(s) failed — ${FAILED_SCRIPTS[*]}\"\n221\t notify \"Array start on $(hostname) ($MY_ID) — $FAILED script(s) failed: ${FAILED_SCRIPTS[*]}\" \\\n222\t \"Array Start\" \"warning\"\n223\telse\n224\t echo \"$ICON_DONE Status: all $LAUNCHED script(s) launched ✅\"\n225\tfi\n226\techo \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\""}]},"uuid":"480d10a8-8fbe-4a65-88f6-e19a3a4efe0c","timestamp":"2026-06-06T17:53:31.275Z","toolUseResult":{"type":"text","file":{"filePath":"/boot/config/plugins/varaverk/Orchestrators/array_started.sh","content":"#!/bin/bash\n# ==============================================================================================\n# ================================= Array Start Orchestrator ===================================\n# ==============================================================================================\n# Single entry point for array start — fired by the Varaverk plugin's\n# disks_mounted event hook (Plugin/unraid/event/disks_mounted/array_start_jobs).\n# Launches everything configured in ARRAY_START_SCRIPTS in master.conf.\n# This script exits after launching all scripts — the event hook sees it complete normally.\n#\n# ── WHAT IT LAUNCHES ──────────────────────────────────────────────────────────────────────────\n# Configured in master.conf ARRAY_START_SCRIPTS — no changes to this script ever needed.\n# Current order (order matters — see below):\n#\n# ONE-SHOT (run and exit naturally):\n# System_Essentials/unraid_api_key_renew.sh — re-register Varaverk API key at boot\n# System_Essentials/inotify_tuning.sh — raise inotify limits before containers start\n# System_Essentials/docker_syslog_filter.sh — suppress veth log noise before logs fill\n# System_Essentials/php_fpm_max_children.sh — WebGUI performance tuning\n# Transcodes/ramdisk_setup.sh — create tmpfs + symlink before Emby starts\n# Docker_Essentials/docker_network_connect.sh — ensure networks + container connections\n#\n# CONTINUOUS (run until array stops):\n# Fallback/fallback.sh — mutual fallback monitor\n#\n# NOTE: watchdogs (docker, system, stability) are NOT launched here.\n# They run via watchdog_orchestrator.sh every 15 min (cron), not as daemons.\n#\n# ── WHY ORDER MATTERS ─────────────────────────────────────────────────────────────────────────\n# unraid_api_key_renew.sh — before anything else — self-heals API registry at boot\n# inotify_tuning.sh — must run BEFORE Code-Server and other containers start\n# containers that start with low inotify limits keep them ✅\n# docker_syslog_filter — must run BEFORE any container starts creating veth interfaces\n# ramdisk_setup.sh — must run BEFORE Emby starts transcoding\n# docker_network_connect — must run BEFORE watchdogs check container states\n# fallback.sh — last — needs everything else stable to make decisions\n#\n# ── ONE-SHOT vs CONTINUOUS DETECTION ─────────────────────────────────────────────────────────\n# Script is launched in background with bash script.sh &\n# After 1 second: if PID still alive → continuous (running in background)\n# if PID dead + exit 0 → one-shot completed successfully\n# if PID dead + exit N → failure\n#\n# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────\n# Root check — all launched scripts require root\n# acquire_lock — prevents duplicate array start launches\n# detect_hosts() — MY_ID in notifications\n# platform_require_cmd — notify validated before use\n# chmod +x auto-fix — non-executable scripts fixed before launch\n# Full path on failure — shows exact path for debugging\n# notify on failures — alert if any script fails to launch\n#\n# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────\n# ARRAY_START_SCRIPTS — ordered list of scripts to launch at array start\n#\n# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────\n# array_started.sh — normal launch (called by Varaverk disks_mounted event hook)\n# array_started.sh --dry-run — show what would be launched without launching\n# array_started.sh --status — show configured scripts and their current state\n# array_started.sh --log — verbose output per script\n# ==============================================================================================\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nECOSYSTEM_ROOT=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\n\nsource \"$ECOSYSTEM_ROOT/load_config.sh\"\n\nparse_args \"$@\"\n\n# ==============================================================================================\n# ━━━ Setup ━━━\n# ==============================================================================================\nif [[ \"$EUID\" -ne 0 ]]; then\n error \"Must be run as root\"\n exit 1\nfi\n\nplatform_require_cmd \\\n \"/usr/local/emhttp/plugins/dynamix/scripts/notify\" \\\n \"\" \"\" \\\n \"unRAID notify script\" || warn \"unRAID notify script not found — native notifications disabled\"\n\nacquire_lock\n\ndetect_hosts\n\n[[ \"$DRY_RUN\" == true ]] && warn \"DRY RUN — scripts will not be launched\"\n\n# ==============================================================================================\n# ━━━ Status ━━━\n# ==============================================================================================\nif [[ \"$SHOW_STATUS\" == true ]]; then\n echo \"\"\n echo \"━━━━━ $ICON_SUMMARY ARRAY START STATUS ━━━━━\"\n echo \"$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)\"\n echo \"$ICON_GEAR Scripts: ${#ARRAY_START_SCRIPTS[@]} configured\"\n echo \"\"\n\n for relative_path in \"${ARRAY_START_SCRIPTS[@]}\"; do\n [[ -z \"$relative_path\" ]] && continue\n script_path=\"$ECOSYSTEM_ROOT/$relative_path\"\n script_name=$(basename \"$script_path\")\n\n if [[ ! -f \"$script_path\" ]]; then\n echo \" $ICON_ERROR $script_name — FILE NOT FOUND\"\n echo \" $script_path\"\n continue\n fi\n\n [[ ! -x \"$script_path\" ]] && flag=\" (not executable — will auto-fix)\" || flag=\"\"\n\n # Check if currently running\n if pgrep -f \"$script_path\" >/dev/null 2>&1; then\n RUN_PID=$(pgrep -f \"$script_path\" | head -1)\n echo \" $ICON_RUNNING $script_name — RUNNING (PID $RUN_PID)${flag}\"\n else\n echo \" $ICON_NOT_RUNNING $script_name — not running${flag}\"\n fi\n done\n echo \"━━━━━━━━━━━━━━━━━━━━━━━\"\n exit 0\nfi\n\n# ==============================================================================================\n# ━━━ Launch Scripts ━━━\n# ==============================================================================================\nif [[ ${#ARRAY_START_SCRIPTS[@]} -eq 0 ]]; then\n notify \"Array started on $LOCAL_SERVER_NAME ($MY_ID) but ARRAY_START_SCRIPTS is empty — boot sequence skipped. Check master.conf.\" \\\n \"Array Start\" \"alert\"\n error \"ARRAY_START_SCRIPTS is empty — check master.conf\"\n exit 1\nfi\n\necho \"\"\necho \"━━━ $ICON_GEAR Array Start — $MY_ID — $(date '+%Y-%m-%d %H:%M:%S') ━━━\"\nlog \"Ecosystem root: $ECOSYSTEM_ROOT\"\nlog \"Launching ${#ARRAY_START_SCRIPTS[@]} script(s)...\"\necho \"\"\n\nSTART=$(date +%s)\nLAUNCHED=0\nFAILED=0\nFAILED_SCRIPTS=()\n\nfor relative_path in \"${ARRAY_START_SCRIPTS[@]}\"; do\n [[ -z \"$relative_path\" ]] && continue\n\n SCRIPT_PATH=\"$ECOSYSTEM_ROOT/$relative_path\"\n SCRIPT_NAME=$(basename \"$SCRIPT_PATH\")\n\n # File existence check\n if [[ ! -f \"$SCRIPT_PATH\" ]]; then\n error \"$SCRIPT_NAME — not found\"\n error \" Expected: $SCRIPT_PATH\"\n (( FAILED++ ))\n FAILED_SCRIPTS+=(\"$SCRIPT_NAME\")\n continue\n fi\n\n # Auto-fix permissions — chmod +x if needed\n if [[ ! -x \"$SCRIPT_PATH\" ]]; then\n warn \"$SCRIPT_NAME — not executable, fixing...\"\n chmod +x \"$SCRIPT_PATH\" || {\n error \"$SCRIPT_NAME — chmod +x failed\"\n (( FAILED++ ))\n FAILED_SCRIPTS+=(\"$SCRIPT_NAME\")\n continue\n }\n fi\n\n if [[ \"$DRY_RUN\" == true ]]; then\n warn \"DRY RUN — would launch: $SCRIPT_NAME\"\n (( LAUNCHED++ ))\n continue\n fi\n\n log \"$ICON_START Launching $SCRIPT_NAME...\"\n bash \"$SCRIPT_PATH\" &\n PID=$!\n\n # Brief settle — 1s enough to detect immediate failures\n sleep 1\n\n if kill -0 \"$PID\" 2>/dev/null; then\n # Still running → continuous script\n warn \"$SCRIPT_NAME — running (PID $PID) ✅\"\n (( LAUNCHED++ ))\n else\n # Exited — check if one-shot success or failure\n wait \"$PID\"\n EXIT_CODE=$?\n if [[ \"$EXIT_CODE\" -eq 0 ]]; then\n log \"$SCRIPT_NAME — completed (one-shot) ✅\"\n (( LAUNCHED++ ))\n else\n error \"$SCRIPT_NAME — exited with code $EXIT_CODE\"\n error \" Path: $SCRIPT_PATH\"\n (( FAILED++ ))\n FAILED_SCRIPTS+=(\"$SCRIPT_NAME\")\n fi\n fi\n\ndone\n\nEND=$(date +%s)\n\n# ==============================================================================================\n# ━━━ Summary ━━━\n# ==============================================================================================\necho \"\"\necho \"━━━━━ $ICON_SUMMARY ARRAY START SUMMARY ━━━━━\"\necho \"$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)\"\necho \"$ICON_SUCCESS Launched: $LAUNCHED\"\n[[ \"$FAILED\" -gt 0 ]] && echo \"$ICON_ERROR Failed: $FAILED — ${FAILED_SCRIPTS[*]}\"\necho \"$ICON_TIME Duration: $(format_duration $(( END - START )))\"\necho \"\"\n\nif [[ \"$DRY_RUN\" == true ]]; then\n warn \"DRY RUN — no scripts launched\"\nelif [[ \"$FAILED\" -gt 0 ]]; then\n warn \"Status: $FAILED script(s) failed — ${FAILED_SCRIPTS[*]}\"\n notify \"Array start on $(hostname) ($MY_ID) — $FAILED script(s) failed: ${FAILED_SCRIPTS[*]}\" \\\n \"Array Start\" \"warning\"\nelse\n echo \"$ICON_DONE Status: all $LAUNCHED script(s) launched ✅\"\nfi\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"","numLines":226,"startLine":1,"totalLines":226}},"sourceToolAssistantUUID":"b327df86-1769-4004-8e13-94a48b5b5036","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"last-prompt","lastPrompt":"yes, do that. and lets make sure our claud script loads it on an unraid reboot","leafUuid":"480d10a8-8fbe-4a65-88f6-e19a3a4efe0c","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"ai-title","aiTitle":"Troubleshoot NPM authentication failure","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"mode","mode":"normal","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"permission-mode","permissionMode":"default","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"file-history-snapshot","messageId":"1c0a9cf6-2842-45e7-b7b6-f25df0cadabd","snapshot":{"messageId":"12dacd63-8642-4c62-bbf1-88a1adb3205e","trackedFileBackups":{"/boot/config/plugins/varaverk/CLAUDE.md":{"backupFileName":null,"version":1,"backupTime":"2026-06-06T17:54:42.568Z"}},"timestamp":"2026-06-06T17:53:16.280Z"},"isSnapshotUpdate":true} {"parentUuid":"480d10a8-8fbe-4a65-88f6-e19a3a4efe0c","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_017yXGWgKpNrdpabgzJUNKps","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"EqcmCmUIDhgCKkAG6fLLvHnETbfRxauZRJ/sz/ZNIYJ9nXxcQZik2rxm+T+K/ucqUSzyFuDQbh1qIt2eSL+JhzWoRq69v+kCQkXEMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIMSLtXQU0A7i1Mn5/aGgxk3UfgLkDbxgtfnfEiMAyZtIu4cgl76QpZl7M00jM+N61s/fApugXPvD5mgdZHSEdQHJLDpKfLa5l1eByU6irvJE8xgEmwWdYegiI5o41MdGTZ7EdH5w1uMf4aAD07/ZALOhKO9igMLbvSDmZY7ludzZv3TGjx8+GDTcDqKPud9Twh8SsozUAd8oAxumbuX4WDuMAiP9/2+uxxxe61x6TOOLQU7oAkHdjzfCYjEkekN8pcU0jXbj9Sgxl/nrcFqtNqUh/JEHat2YN05JjkTt80dJhwlrUd2sS7jmDbKZqHBoxOGWwz0Nwy5DQ1wmuIXEupHVgxJYN8gj/yr39yzWI1GVtnA+3cgZ7LqLs4W+agFYXERxXVSSvNJQOJjt/EQj6+1Ayf75CL+26KhTyfFQABUJnD4PwVqMHDguE/3BMylO0j3OYCFf+wl2FfjKEllC7jueug40q/kpzlYJi+yDz2BaDTsgdpae0xaRpuJOjeGSa8gAoDgRyRydNxK/eOhApNlBqT1gImrRPapvCB8U6nM2B+Aj5pRsvjY6arbDPzQIjvPPqrDzGG/0bb5aEAZbv6UZWveFzYTtBfpg/HsUCXLESXegVCJXEokznpv+/SGAb9J0LkXBe2C2PrMtqKUzR4Bgsz8tiUiO7xQ+5VCDPnFuOmUM1SuDt45Cr3j6Tz/zk9j8IfQuTQoHp4xSyQOP8suv7RRoDmCQcrhnt2A0pb5GUeyxu3WVVInnPgreVdbeOZ5RP0YQyrPC9ak5TcpBwzDKjK34hlFQBAboHGTqU+LHt8ebuS+68sLjsTpH+PPRr5nKNTr7lhgmL8Zpm4RcQAcfEdzkpsbQ3641frBG0eAtX0DR27uQy7vdGUn6Td1dCkCA6IZgxVWuQVIPBWt04GzQxCMiugBVTGKEuEhnnr8/vWkloTw1qZi7Y+sMpQovCNo1CqRJ1L6H/kvG6ZE/24wSEyHGBXWhrX5yanG5Ho36yyDuQk43C9RXLy/4cWbE2h5I24UHadScrbQ+aCwZTyKgKxY/vdY/vshuQ2NRNc6jLbGTcxt6NupdsfmZgn2YOsJTl4s7uUp3Ho4RqGgj0wuH+bPdjZukbz7soucKkuFtzJomMC+4XbnmjAjflygkg1h4cEJ6hGbyXWwMUeLCnl2I7tpoozbx6xfUfOTfHtSv57/Uk2D3Urv7e8oR7objrg5tXaaXO3xjclkX4XKKrRlcFEhKKVio/4Psd2PZjouDICw0qhhtYCl1ocVXuiheMExjJ+h9uz77Z/u6ZQBi8lhLvpcgfJgFWS1Tlgdbxg+8Eqb6h1qlGipC02N3iAHaYgK0G2+BqVBHCxHFFr2QcIikmbEkf+dHx3La5AdwCuHBJyZgn/Zyt+B2hjVJYoxDLXC12tTz2F5F/KaIw8RmKJHM9G0uS1bJxtD0k0ATeQDg+MNqi21hcwD1wFF/xzKyjAyaNSaWbbXKeKYSzWzTIl7W5m/9dISJnQVQjp1a2VtngLNQ1ZU9Wqtb8zbTuZTlh6lXdvsLVyCfUYWRLzEkR2vZlO6m3W+Yz3w5iWlWk16ociJFTi2hpbbLVMIm1Y46zoHzaezs2nEXunyr5IrGOSt88a6dviMnxPVS1kjPmU61yLCxNvqIpVyRTq+KmftvebycCTo84j0CjIpLnDzZGznVMASrz3N7VZO/2k698KO+Dp7mwxXNgdOl5ACXUUSxU6fFmzqxTY9Km+ar2GekiZzqRjhsFV3xIuCpf5ZfOoRorTozOBjRdTai0N/o2RW3e66muJCR4ut9an1vr+dvRkZX8aWCJzZSqpwtegHXWqDWoT7D3ywnX/sFusdolgIOHipOQacN9u3Zc8K/hBsSOXeV4AklsG5MxErmunmm8su6YhfynzX7zKAUoMlM/cJaamoDZMqTbxiHrVfjjAFbRls1pbr0tmsIHl2aPQMMfDeuCA4AW9takAPek9G3iCm2aI6awM335PlcY4y7jNA5LnjSowEqIQ9a0fq8rRWKG78ZkRWYJG5ubdldbIrzAoKzjsUKClSF1bQeO502TL7/H3w+auCf/Hos5wRpnoKwTYG2EgHMpR6On9fViohxI/+Js8y3qLVOV5kPSK+A66VYYSn668M+NuJxnaXIKEafUt2gcJJXpecPI6RwbzOekbHkYDiuVM7BzS2PUZbNm7qQ8hts+93MLZuan4mExRDRwh+0no2nt8ZlQLfhpcIM1gRaQnsT87aRvVxBT/ZOPW1e8lcTJ/XeKeFYlUNUGOr48nIcQsg/D7ffOIp8DXv31196m1gOQz9WtZUOtmUHLyFJ14JMTE0/7EMsYzHJ10TEli80nrkuo9i0H4+dTLcmCw840Ds+OjDUJ3WBI2bgegaDj3WyEUXuEAgK2cU8/iZtbeoe3OBUBgk/EZfikNxYVf2pd8k3Tfk2b+h8x+4ZKBJNpbGFskhSayWzCVez3UT37irB0eveHmGSmab7jZYcXPfwAjE0+pisaQ7riz7WQhI/LN5qGl/xZEClEPVCynj8My/jIuMHVEqw+n23N0hSHzHfgbdV1Edhl3S8OEJxdT9N6KEWh2ieZWh6FpN+Wut13SXvZOpxctS2EbLL80C3xWb7Uu+Fqu9mzwCll7MoqX9DLLYDVuMH3+QZdLcB56AstgMaWwNXvw7gW22l0R0KChv3ExxwR9gXt5wyVSU78jBbP7ptwTDSd5Hb1RQTZoPSJYzlHgJOtD5PQ3JrHO+NxLw/5F7SSvz/xDM5UPx6qduptfnSjn8aKF7C2Ca/p/szKEuA+B+/tmCULgrxhzaJA7tGu1TCdVOZP9wUg/LK3HYTojacFh/GIg7zAQHk+i5ibqdbZBb1+ZqXdPjLAz4SGbjqXiszJDQk7y6w1/w7PWXoq+FGR6dtzJpaRWXpTEfkSoGDA3hS25reHtfc/acrySBXOAX3qwmEp0jdrSWroHZRKd0pwM1NEv+Cj4SqPkwlhXPv63coigpX8IqqvKMqoOv0S8BTHb9qoPmY5e5CY64UfUzYM11JPejnBiESIlYK3gJ//7fijKNGs00zcFoUzu98Fu1Dcwox1eQwFTndwAIpEi6I18LILeAKOcj9sEiQDEd/uulngIuoTfDMh3aNFy5YbRC8w2fsJE15pCNMT2sAHsosKMrh0sAoBWzGVx485UtjE+KGlpjt4ED0kBLwElUBm/x4+sEs5TiYp2MlGwrZgH/RIIkaxGUiKvWMU2Mj063uKPychxSs7qJG2ldkdxvH0Uf4jTgO3EBXQiIOZFY6Qi9S5NWSm4u2bGpR5a8VGrR8n8c0feTa1L/RaRUBLs2k2+8D3AQMACbLA2v84mDJTCIZG+AIaoWNG0hGQ/hnu68W2i4FdQOuXOCN27EALhmZfLQpZIGyUvVp/FRDrnJNBcD5HIcvFgKF1t23/bWPUP+ZfYlawjre40IgAlKiiQqobt9PW91cMqP5MQw79uDOwkN4co4os8kq2zrA5eCd23nkdT4SR1AfEbLijlI/EMKYRcdC1ukOaZBfkB52zo5U/vJbfZxniK9hAhOO/fO5dE3w18KUHq5CUxnLoYk3zdnNm9/O0ac5nHrfwl5hchfM3vCUzCeQjAJH4F05ueklSwIjTGqGO2CfnnaBeIjuUSK1vagUMgT7m96YSZNvUxaDkldqxGzXC+UsL6T21d4mc2fm9o3gN+EReAkw6ZocKWRig1LwV43uzL3nU0Q4Xo9wOT8qsFYRkiFr/0/2aOa15ouFfy3ljBNeMINTec+7lEX4v2iWidSNg0CM2DVEb5YXTjn3ZwtsZGwp/ljC19QOu264kw48FBGvzopKbfWtgX03nOvYfaV4MK578KDtqe3alfsu5vaZlQ+mT61TiLF4uGFTrSVkn8PdF39knu85JvnXq5gHA0AhSkwHtmFd8lO/9eDYTpliN21f1Lr5MOdxN68CQi63g4hfiwKZtBQjlLu0WJaCrOqK7AOmDbK7MFk/H6mYHELNmtCsitvOko5JSRGIfrqKcbqlYT3aAzYPiSdbI5sk9+ztKgxaPCmmFQWBrSWfEDmSa4hYltfS0rYQSEFnXlfyhFuBLcRu2W5a3WYhpkQiHMfazTx8ygwdiLSI3rp203A0p1gsxCpBRA5u3bpUvMvD9Qo8vx5c4oJxa+mNbpqARLGVAlXzw7E0eT1CD/XYvWAmUjdJ1c2VSX0HzHtUyluUPcejuU3uHR0+hW6btIqsuW4Wcqrc2bfiCuwHGtudAtLyodFOd3peCid3wH446ET3ORiVoYxrTDGWOLI5BhB0gFWisYEvgvGxVpC1O4l8BsGHQdhnZDMgD4n3cAk7USVO1HAhMFpjKwdc0DDR7cOTB+rfLm68292orIzT/K4ftBuVdbTY59z9d2qIQB56y2OOEYN0SB1TpnfCsTXwkH6dHtNXkc94ZmIespyLba+9ZZqoYKVGBIINRCk606DGwIakab43KPYLXMvu5DlS8YfJ3YeT1WbIn6xDam1KTjqqb86G88ohRAo26e8B6cIWBUHWUZt3JqCrGBG3SuV7R2BllBgcXleK1s9Orb6oZnWnaNv5K/5MhWpvz/lrpHBYVYglrq0+loRY/FdM/FpaQRa7swDgCPvVQcLi4KApkOh3OaMKp2EB4iZns/IENgfkkUq6PZ8syYWAoOEVeDbd2T9Vx2dbcD3NAz83bfZiDEsOzmhdSfGgylqXK5xcHdHLYoYP+/+9+Pj5jFP3iHmVaALV8BAYzujVsNMScoXnP6SevLUUXjDBjo9np9NlcYlfli/Fnx0qikzziKmcKmeBskPuR1aU6tRm2XmGWpFBcJlBB+76CN6VVg4JWJLmPADud3xMjnFXULtOU8vz9prmlh9FHvZE4Oxt3GbB6xSuMHzZ2QVBGFLhUQ1g5hWN13GAQoGog1PTeIfZKJyoZ5bJku3dUj+znLbX6JnY8E3DBAjGaAwlTyLdFbpGhs+w3uX8h921eJwp573bi/U2WDvpGJzpkW9PGjWcyGZKNFnEesU8OPZSIgBp8U2TIsgPv+Q1gQFeALi4INh5LR27IT+51qCUexfOnfLoMZWIOAKvFxH0WfBUXpMuw81JbHIQ74nA7vamq2bVklksu3ehovMxQb4SiO+5ysu+Bf3OJrr+CM69zvpjIbvUIFuOVjEPKLXGk593NiX/aHaZXxWs/BR3kSJ8gFt267MQEqtJ26bT4d7FxohyTpMDEiDX1tMTmnh+lqpBbEjkGIJhEhRA0aSEHvjrUJcUhikrSHbPyANjbLQQgTkhKUVVm6mdnD/PKULnWRMgId/9oq7fS8qWhsjkIDcQypY3uiDlq6YG74WCDrHo3UcI9JkTWiV3v3r0RqDl3twOnKxCqTqUtibJqeix/FfPjBzv4/tdnJrNR4LANy6zkfatXs20FqlVfqcFAKjjY6LnyWtxhwWawCpuMR/sYFwt9K+3PLyOKaqC+HufQYNzDn/lTqzxRjgCM0ed2REZCd5O0gudyj7cVsRCAPExssngf6V9Ir4Uagp1wezAwcCGWJlhChhqTiIkdf7bKXAxkqf8cxygbnQ99GPuOBcAiiWBExb5fpemKv0z7HexCECJ8nscLT9lL03cc8jjgxL/eZsO58wWUb9q9gcn6uSpihuTrVxQMIx0qJQCekMyndFisSO/TqHSVciakvIGM5xEFh5FJG4/DvSaUfQ/VosJ2EMqFwwKs/hSWmNmvmaw0DPe2YXa8j2eVOU42kU0xGm32DDcdGdBelBefRX7JAYgBAhnjOJDRiMcJSdvWZs2zaJY0apLTz5YV8u4GveKrkI9qPqwTjcshBsrFD7JDgJIeElavZWvIBFE7GC2vauKAw7zPeYWP3d3VtSBUPrh7MuzYffpDg78U02WwSx4uIF4gBDjCTNhoX9EWECDtiYPWvGzmSDYuk5GTUbzG2Iy9xGiaWNuxOZz6o3T5UiHtlQEWROaNmMsWbbrq6rnzsNEJ7ER799HnkW+b5Cjv5IH9kir0PsrgbpCCxvPFfi8PTA3ZE7/+zNEMMAPV43StB14V7nfJcUfCqy1biNHhD6w2R1lltvFH+rV/dHG8i/es0ifOeVuV20IeGAIo2ksPkNPS3pFBAx8S3KgGlKTMkpcACNPsA9qoqfCXhmQeMBAez79H9JiPL0cYpn504EZ3CXJOk/K2hf3Alh25BSjaY3sb3iUBodT6HXbyw/OSN1Uk6ByOJRM0WLJxm6a2rgH2QQ1Xf7xf1jINfjz2v3I4oCjhnTvwFQNhhkRS7CatTofgZTwVybtgOzzAUJ3xklnYUO3+df2HJxFUJZV3bsEEWpk8yE9mr7d2KVhvx7akG2IYsRGG33BYrUha0FXRsxUzMJjHqHaCmij8HwPNrB/zst5C/xBcxMrzQvsJElyGNyBq9rhgB"}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":4022,"cache_read_input_tokens":84673,"output_tokens":3385,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":4022,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":3385,"cache_read_input_tokens":84673,"cache_creation_input_tokens":4022,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4022},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS72u6CkGmAptTMnzGz","type":"assistant","uuid":"8198102c-0eb9-4bf2-9ecb-414919e3354c","timestamp":"2026-06-06T17:53:56.087Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"8198102c-0eb9-4bf2-9ecb-414919e3354c","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_017yXGWgKpNrdpabgzJUNKps","type":"message","role":"assistant","content":[{"type":"text","text":"Good picture of everything. Now I'll write the CLAUDE.md to `/boot/config/plugins/varaverk/CLAUDE.md` and update `claude_startup.sh` to symlink it to `/root/CLAUDE.md` on every boot so it's always loaded."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":4022,"cache_read_input_tokens":84673,"output_tokens":3385,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":4022,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":3385,"cache_read_input_tokens":84673,"cache_creation_input_tokens":4022,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4022},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS72u6CkGmAptTMnzGz","type":"assistant","uuid":"ac5db344-a86e-4955-b3fc-1542a8d75fac","timestamp":"2026-06-06T17:53:57.415Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"ac5db344-a86e-4955-b3fc-1542a8d75fac","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_017yXGWgKpNrdpabgzJUNKps","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01A6xKFFH6DM7U9AR32PD7hS","name":"Write","input":{"file_path":"/boot/config/plugins/varaverk/CLAUDE.md","content":"# Varaverk — Claude Code Context\n\n## Working Rules (read first)\n\n- **Workspace is always** `/boot/config/plugins/varaverk` — every edit goes here.\n- **Never touch** `/mnt/user/Important Shit/Git/Development/Varaverk` — stale dev folder, ignore it.\n- **No Co-Authored-By** in commit messages unless explicitly asked.\n- **No comments** unless the WHY is genuinely non-obvious.\n- The `.plg` symlinks the installed plugin location directly to this workspace — one copy, no drift.\n\n---\n\n## Project: What Varaverk Is\n\nSelf-healing, self-maintaining, mutually-redundant two-server Unraid home media ecosystem.\nOne codebase runs on both servers. No primary/standby — both run independently and cover each other.\n\n**HOST1 — unRAID-Gmer4Lfe** (`gmer4lfe@gmail.com`)\n- Hardware: Threadripper 1950X, 128 GB RAM, ZFS cache pools\n- Domain: Gmer4Lfe.com\n- Runs: full arr stack (Sonarr/Radarr/Lidarr), auth stack (source of truth), Emby primary\n\n**HOST2 — unRAID-Jayred365**\n- Hardware: Intel i5 10th gen, 64 GB RAM\n- Domain: Gmer4Lfe.us\n- Status: being rebuilt — most host2.conf sections scaffolded, not yet fully online\n\nNetworking between hosts: Tailscale mesh. No hardcoded IPs — hostnames resolve via Tailscale.\n\n---\n\n## Configuration System (three-file model)\n\nEvery script sources all three at startup:\n\n```\nmaster.conf ← shared: thresholds, toggles, profiles, orchestrator job lists\nhost1.conf ← HOST1 credentials, shares, container names, keys\nhost2.conf ← HOST2 credentials, shares, container names, keys\n```\n\nSparse checkout (git) means each server only pulls its own `host*.conf`.\nHOST1 never sees HOST2 credentials and vice versa.\n\n**Rule:** thresholds/toggles → `master.conf`; credentials/paths/container names → `host*.conf`.\n\n`detect_hosts()` in `common.sh` matches `$(hostname)` against `HOST1`/`HOST2` in `master.conf`\nand sets `MY_ID` / `REMOTE_ID` for the rest of the script.\n\n---\n\n## Platform Adapter Layer\n\n`Plugin/unraid/adapter.sh` isolates all OS-specific calls.\nScripts never branch on OS directly — always call adapter functions.\nThis is intentional architecture — don't bypass it.\n\n---\n\n## Key Paths\n\n| Path | Purpose |\n|------|---------|\n| `master.conf` | Shared config — all thresholds, toggles, profiles |\n| `host1.conf` / `host2.conf` | Per-host credentials, shares, container lists |\n| `common.sh` | Shared functions — `detect_hosts()`, `log()`, `notify()`, etc. |\n| `load_config.sh` | Sources all three conf files + common.sh |\n| `State_Files/` | Runtime state (watchdogs, fallback, transcode) — survives reboots |\n| `data/` | Historical logs and stats |\n| `Plugin/unraid/` | Unraid WebGUI plugin (PHP pages, API endpoints, adapter) |\n| `Orchestrators/` | Top-level schedulers (array_started, daily, weekly, watchdog) |\n| `Watchdogs/` | docker_watchdog, system_watchdog, resource_watchdog, stability |\n| `Fallback/` | Mutual container failover logic |\n| `Rsync/` | rsync.sh + profile system |\n| `Media/` | Arr cleanup, discovery, permissions, play state sync |\n| `Tools/` | Manual one-off tools including `claude_startup.sh` |\n\n---\n\n## Orchestrator Schedule\n\n| When | What |\n|------|------|\n| Array start | `Orchestrators/array_started.sh` → runs `ARRAY_START_SCRIPTS` |\n| Every minute | `watchdog_orchestrator.sh` → resource → docker → system → stability watchdogs |\n| Every 30 min | `critical_sync_maintenance.sh` → downloaders_reset, play_state_sync, critical rsync |\n| Every 4 hours | `intermediate_sync_maintenance.sh` → arr_sync, arrs_failed_stalled_recovery |\n| Daily 1am | `daily_sync_maintenance.sh` → git pull, permissions, cleaners, arr cleanup, docker updates |\n| Sunday 2:30am | `weekly_sync_maintenance.sh` → full Emby + Critical-Data sync, weekly restarts |\n| Sunday 3am+ | `monthly_maintenance.sh` (self-gated on 30-day uptime) → ZFS scrub, SMART tests |\n| Sunday 7am | `sunday_morning_coffee_report.sh` → ZFS, SMART, certs, backup verify, bandwidth, Emby report |\n\n---\n\n## Rsync Toggle State (current)\n\n```bash\nRSYNC_ENABLED=true\nCRITICAL_RSYNC_ENABLED=true\nINTERMEDIATE_RSYNC_ENABLED=true\nDAILY_RSYNC_ENABLED=false # HOST2 rebuild in progress — re-enable when ready\nWEEKLY_RSYNC_ENABLED=true\nFALLBACK_RSYNC_ENABLED=true\n```\n\n---\n\n## Fallback System\n\n`fallback.sh` runs continuously from array start.\nStates: `NORMAL | FALLBACK | NO_INTERNET | DARK`\n\nDDNS rules are absolute:\n- Internet loss → stop own DDNS immediately\n- Failover → start remote's DDNS as Tier 1 first\n- Handback → stop remote DDNS → rsync → start containers → start local DDNS last\n\nTier delays before activating higher tiers are in `host*.conf` (`HOST1_TIER*_DELAY`, `HOST2_TIER*_DELAY`).\n\n---\n\n## Known Gaps / Active Work\n\n- **host1.conf is missing the NPM auth credentials section** — no `HOST1_NPM_URL`, `HOST1_NPM_USER`, `HOST1_NPM_PASS`. `auth.php` reads `{$host}_NPM_USER/PASS/URL` — HOST1 falls back to empty strings. Needs the section added (same pattern as host2.conf lines 643–661).\n- HOST2 has the NPM section but `HOST2_NPM_USER` and `HOST2_NPM_PASS` are empty — fill in when HOST2 is back online.\n- `PARTNERSHIP_ENABLED=false` — not yet active.\n- `FALLBACK_ENABLED=true` — fallback is running.\n- `DAILY_RSYNC_ENABLED=false` — paused during HOST2 rebuild.\n\n---\n\n## Claude Code Persistence on Unraid\n\n`/root` is a RAM filesystem — wiped on every reboot.\n`Tools/claude_startup.sh` runs at array start (via `ARRAY_START_SCRIPTS`) and:\n- Symlinks `/root/.claude` → `/mnt/user/appdata/claude-code/.claude`\n- Symlinks `/root/.local/share/claude` → `/mnt/user/appdata/claude-code/local/share/claude`\n- Symlinks `/root/CLAUDE.md` → `/boot/config/plugins/varaverk/CLAUDE.md` (this file)\n\nThis file lives on `/boot` (USB flash) and is always available regardless of array state.\n\n---\n\n## Commit Style\n\nPlain, concise messages. No Co-Authored-By trailers. No bullet-point summaries in the body.\nOne sentence on the why, not the what.\n"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":4022,"cache_read_input_tokens":84673,"output_tokens":3385,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":4022,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":3385,"cache_read_input_tokens":84673,"cache_creation_input_tokens":4022,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4022},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnS72u6CkGmAptTMnzGz","type":"assistant","uuid":"1c0a9cf6-2842-45e7-b7b6-f25df0cadabd","timestamp":"2026-06-06T17:54:36.935Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"1c0a9cf6-2842-45e7-b7b6-f25df0cadabd","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01A6xKFFH6DM7U9AR32PD7hS","type":"tool_result","content":"File created successfully at: /boot/config/plugins/varaverk/CLAUDE.md (file state is current in your context — no need to Read it back)"}]},"uuid":"ef3b2501-d4f4-4a10-ac21-be55c8b0d0c8","timestamp":"2026-06-06T17:54:42.619Z","toolUseResult":{"type":"create","filePath":"/boot/config/plugins/varaverk/CLAUDE.md","content":"# Varaverk — Claude Code Context\n\n## Working Rules (read first)\n\n- **Workspace is always** `/boot/config/plugins/varaverk` — every edit goes here.\n- **Never touch** `/mnt/user/Important Shit/Git/Development/Varaverk` — stale dev folder, ignore it.\n- **No Co-Authored-By** in commit messages unless explicitly asked.\n- **No comments** unless the WHY is genuinely non-obvious.\n- The `.plg` symlinks the installed plugin location directly to this workspace — one copy, no drift.\n\n---\n\n## Project: What Varaverk Is\n\nSelf-healing, self-maintaining, mutually-redundant two-server Unraid home media ecosystem.\nOne codebase runs on both servers. No primary/standby — both run independently and cover each other.\n\n**HOST1 — unRAID-Gmer4Lfe** (`gmer4lfe@gmail.com`)\n- Hardware: Threadripper 1950X, 128 GB RAM, ZFS cache pools\n- Domain: Gmer4Lfe.com\n- Runs: full arr stack (Sonarr/Radarr/Lidarr), auth stack (source of truth), Emby primary\n\n**HOST2 — unRAID-Jayred365**\n- Hardware: Intel i5 10th gen, 64 GB RAM\n- Domain: Gmer4Lfe.us\n- Status: being rebuilt — most host2.conf sections scaffolded, not yet fully online\n\nNetworking between hosts: Tailscale mesh. No hardcoded IPs — hostnames resolve via Tailscale.\n\n---\n\n## Configuration System (three-file model)\n\nEvery script sources all three at startup:\n\n```\nmaster.conf ← shared: thresholds, toggles, profiles, orchestrator job lists\nhost1.conf ← HOST1 credentials, shares, container names, keys\nhost2.conf ← HOST2 credentials, shares, container names, keys\n```\n\nSparse checkout (git) means each server only pulls its own `host*.conf`.\nHOST1 never sees HOST2 credentials and vice versa.\n\n**Rule:** thresholds/toggles → `master.conf`; credentials/paths/container names → `host*.conf`.\n\n`detect_hosts()` in `common.sh` matches `$(hostname)` against `HOST1`/`HOST2` in `master.conf`\nand sets `MY_ID` / `REMOTE_ID` for the rest of the script.\n\n---\n\n## Platform Adapter Layer\n\n`Plugin/unraid/adapter.sh` isolates all OS-specific calls.\nScripts never branch on OS directly — always call adapter functions.\nThis is intentional architecture — don't bypass it.\n\n---\n\n## Key Paths\n\n| Path | Purpose |\n|------|---------|\n| `master.conf` | Shared config — all thresholds, toggles, profiles |\n| `host1.conf` / `host2.conf` | Per-host credentials, shares, container lists |\n| `common.sh` | Shared functions — `detect_hosts()`, `log()`, `notify()`, etc. |\n| `load_config.sh` | Sources all three conf files + common.sh |\n| `State_Files/` | Runtime state (watchdogs, fallback, transcode) — survives reboots |\n| `data/` | Historical logs and stats |\n| `Plugin/unraid/` | Unraid WebGUI plugin (PHP pages, API endpoints, adapter) |\n| `Orchestrators/` | Top-level schedulers (array_started, daily, weekly, watchdog) |\n| `Watchdogs/` | docker_watchdog, system_watchdog, resource_watchdog, stability |\n| `Fallback/` | Mutual container failover logic |\n| `Rsync/` | rsync.sh + profile system |\n| `Media/` | Arr cleanup, discovery, permissions, play state sync |\n| `Tools/` | Manual one-off tools including `claude_startup.sh` |\n\n---\n\n## Orchestrator Schedule\n\n| When | What |\n|------|------|\n| Array start | `Orchestrators/array_started.sh` → runs `ARRAY_START_SCRIPTS` |\n| Every minute | `watchdog_orchestrator.sh` → resource → docker → system → stability watchdogs |\n| Every 30 min | `critical_sync_maintenance.sh` → downloaders_reset, play_state_sync, critical rsync |\n| Every 4 hours | `intermediate_sync_maintenance.sh` → arr_sync, arrs_failed_stalled_recovery |\n| Daily 1am | `daily_sync_maintenance.sh` → git pull, permissions, cleaners, arr cleanup, docker updates |\n| Sunday 2:30am | `weekly_sync_maintenance.sh` → full Emby + Critical-Data sync, weekly restarts |\n| Sunday 3am+ | `monthly_maintenance.sh` (self-gated on 30-day uptime) → ZFS scrub, SMART tests |\n| Sunday 7am | `sunday_morning_coffee_report.sh` → ZFS, SMART, certs, backup verify, bandwidth, Emby report |\n\n---\n\n## Rsync Toggle State (current)\n\n```bash\nRSYNC_ENABLED=true\nCRITICAL_RSYNC_ENABLED=true\nINTERMEDIATE_RSYNC_ENABLED=true\nDAILY_RSYNC_ENABLED=false # HOST2 rebuild in progress — re-enable when ready\nWEEKLY_RSYNC_ENABLED=true\nFALLBACK_RSYNC_ENABLED=true\n```\n\n---\n\n## Fallback System\n\n`fallback.sh` runs continuously from array start.\nStates: `NORMAL | FALLBACK | NO_INTERNET | DARK`\n\nDDNS rules are absolute:\n- Internet loss → stop own DDNS immediately\n- Failover → start remote's DDNS as Tier 1 first\n- Handback → stop remote DDNS → rsync → start containers → start local DDNS last\n\nTier delays before activating higher tiers are in `host*.conf` (`HOST1_TIER*_DELAY`, `HOST2_TIER*_DELAY`).\n\n---\n\n## Known Gaps / Active Work\n\n- **host1.conf is missing the NPM auth credentials section** — no `HOST1_NPM_URL`, `HOST1_NPM_USER`, `HOST1_NPM_PASS`. `auth.php` reads `{$host}_NPM_USER/PASS/URL` — HOST1 falls back to empty strings. Needs the section added (same pattern as host2.conf lines 643–661).\n- HOST2 has the NPM section but `HOST2_NPM_USER` and `HOST2_NPM_PASS` are empty — fill in when HOST2 is back online.\n- `PARTNERSHIP_ENABLED=false` — not yet active.\n- `FALLBACK_ENABLED=true` — fallback is running.\n- `DAILY_RSYNC_ENABLED=false` — paused during HOST2 rebuild.\n\n---\n\n## Claude Code Persistence on Unraid\n\n`/root` is a RAM filesystem — wiped on every reboot.\n`Tools/claude_startup.sh` runs at array start (via `ARRAY_START_SCRIPTS`) and:\n- Symlinks `/root/.claude` → `/mnt/user/appdata/claude-code/.claude`\n- Symlinks `/root/.local/share/claude` → `/mnt/user/appdata/claude-code/local/share/claude`\n- Symlinks `/root/CLAUDE.md` → `/boot/config/plugins/varaverk/CLAUDE.md` (this file)\n\nThis file lives on `/boot` (USB flash) and is always available regardless of array state.\n\n---\n\n## Commit Style\n\nPlain, concise messages. No Co-Authored-By trailers. No bullet-point summaries in the body.\nOne sentence on the why, not the what.\n","structuredPatch":[],"originalFile":null,"userModified":false},"sourceToolAssistantUUID":"1c0a9cf6-2842-45e7-b7b6-f25df0cadabd","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"file-history-snapshot","messageId":"d57bcc7f-011d-440e-9a07-5211de1de929","snapshot":{"messageId":"12dacd63-8642-4c62-bbf1-88a1adb3205e","trackedFileBackups":{"/boot/config/plugins/varaverk/CLAUDE.md":{"backupFileName":null,"version":1,"backupTime":"2026-06-06T17:54:42.568Z"},"/boot/config/plugins/varaverk/Tools/claude_startup.sh":{"backupFileName":"f4b792a0d6c7bbfd@v1","version":1,"backupTime":"2026-06-06T17:54:49.566Z"}},"timestamp":"2026-06-06T17:53:16.280Z"},"isSnapshotUpdate":true} {"parentUuid":"ef3b2501-d4f4-4a10-ac21-be55c8b0d0c8","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01T5pwXKU73E7N3XP6hNDhPR","type":"message","role":"assistant","content":[{"type":"text","text":"Now update `claude_startup.sh` to symlink this CLAUDE.md into `/root/` on every boot."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":3432,"cache_read_input_tokens":88695,"output_tokens":371,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":3432,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":371,"cache_read_input_tokens":88695,"cache_creation_input_tokens":3432,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":3432},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSCHeVSBxcshttt9JzQ","type":"assistant","uuid":"a1660299-42f6-4fd8-a075-7605925bc672","timestamp":"2026-06-06T17:54:45.181Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"a1660299-42f6-4fd8-a075-7605925bc672","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01T5pwXKU73E7N3XP6hNDhPR","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_015juKyCaLgG5DAGuWs8p1By","name":"Edit","input":{"replace_all":false,"file_path":"/boot/config/plugins/varaverk/Tools/claude_startup.sh","old_string":"ln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude\n_log \"claude binary → $CLAUDE_BIN\"","new_string":"ln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude\n_log \"claude binary → $CLAUDE_BIN\"\n\n# ── Symlink CLAUDE.md ─────────────────────────────────────────────────────────────────────────\n# Lives on /boot so it survives reboots without appdata. Symlinked into /root so Claude\n# picks it up automatically from the working directory on every session.\nCLAUDE_MD=\"/boot/config/plugins/varaverk/CLAUDE.md\"\nif [[ -f \"$CLAUDE_MD\" ]]; then\n ln -sfn \"$CLAUDE_MD\" /root/CLAUDE.md\n _log \"CLAUDE.md → $CLAUDE_MD\"\nelse\n _warn \"CLAUDE.md not found at $CLAUDE_MD — skipping symlink\"\nfi"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":3432,"cache_read_input_tokens":88695,"output_tokens":371,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":3432,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":371,"cache_read_input_tokens":88695,"cache_creation_input_tokens":3432,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":3432},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSCHeVSBxcshttt9JzQ","type":"assistant","uuid":"d57bcc7f-011d-440e-9a07-5211de1de929","timestamp":"2026-06-06T17:54:49.541Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"d57bcc7f-011d-440e-9a07-5211de1de929","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_015juKyCaLgG5DAGuWs8p1By","type":"tool_result","content":"The file /boot/config/plugins/varaverk/Tools/claude_startup.sh has been updated successfully. (file state is current in your context — no need to Read it back)"}]},"uuid":"d5d11201-b561-45bc-b9bd-0ff11d308c2e","timestamp":"2026-06-06T17:54:49.641Z","toolUseResult":{"filePath":"/boot/config/plugins/varaverk/Tools/claude_startup.sh","oldString":"ln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude\n_log \"claude binary → $CLAUDE_BIN\"","newString":"ln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude\n_log \"claude binary → $CLAUDE_BIN\"\n\n# ── Symlink CLAUDE.md ─────────────────────────────────────────────────────────────────────────\n# Lives on /boot so it survives reboots without appdata. Symlinked into /root so Claude\n# picks it up automatically from the working directory on every session.\nCLAUDE_MD=\"/boot/config/plugins/varaverk/CLAUDE.md\"\nif [[ -f \"$CLAUDE_MD\" ]]; then\n ln -sfn \"$CLAUDE_MD\" /root/CLAUDE.md\n _log \"CLAUDE.md → $CLAUDE_MD\"\nelse\n _warn \"CLAUDE.md not found at $CLAUDE_MD — skipping symlink\"\nfi","originalFile":"#!/bin/bash\n# ==============================================================================================\n# ================================= Claude Code Startup ========================================\n# ==============================================================================================\n#\n# PURPOSE\n# ─────────────────────────────────────────────────────────────────────────────\n# Restores Claude Code's persistent data after an Unraid reboot and launches Claude.\n# Unraid's root filesystem lives in RAM — /root/.claude and /root/.local are wiped on\n# every reboot. This script symlinks both directories back to persistent appdata storage\n# before launching Claude, so memory, sessions, and settings survive across reboots.\n#\n# On first run with no existing persistent data, migrates from the current live locations:\n# /root/.claude → PERSIST_DIR/.claude (memory, sessions, settings)\n# /root/.local/share/claude → PERSIST_DIR/local/share/claude (installed binaries)\n# Subsequent runs skip the migration and only create the symlinks.\n#\n# Standalone script — no common.sh dependency. Safe to run directly from terminal\n# or from array_started.sh.\n#\n# ==============================================================================================\n# RUNTIME MODES\n# ==============================================================================================\n#\n# claude_startup.sh\n# Set up persistent symlinks only — default, used by array_started.sh on boot.\n#\n# claude_startup.sh --launch\n# Set up persistent symlinks and launch Claude interactively.\n#\n# ==============================================================================================\n\nPERSIST_DIR=\"/mnt/user/appdata/claude-code\"\nCLAUDE_DATA=\"$PERSIST_DIR/.claude\"\nCLAUDE_BIN=\"$PERSIST_DIR/local/share/claude\"\n\nLAUNCH=false\n[[ \"$1\" == \"--launch\" ]] && LAUNCH=true\n\n# Standalone — no common.sh dependency\n_log() { echo \" ✅ $*\"; }\n_warn() { echo \" ⚠️ $*\"; }\n_err() { echo \" ❌ $*\" >&2; }\n\necho \"\"\necho \"━━━ Claude Code Startup ━━━\"\necho \"\"\n\n# ── Array must be mounted ─────────────────────────────────────────────────────────────────────\nif ! mountpoint -q /mnt/user 2>/dev/null; then\n _err \"Array not mounted — /mnt/user not available\"\n exit 1\nfi\n\n# ── Create persistent dirs ────────────────────────────────────────────────────────────────────\nmkdir -p \"$CLAUDE_DATA\" \"$CLAUDE_BIN\"\n\n# ── Migrate .claude on first run ──────────────────────────────────────────────────────────────\nif [[ ! -L /root/.claude && -d /root/.claude ]]; then\n _warn \"First run — migrating /root/.claude → $CLAUDE_DATA\"\n cp -a /root/.claude/. \"$CLAUDE_DATA/\"\n rm -rf /root/.claude\n _log \"Migrated .claude (memory, sessions, settings)\"\nelif [[ -z \"$(ls -A \"$CLAUDE_DATA\" 2>/dev/null)\" && -d /root/.claude ]]; then\n _warn \"Persistent storage empty — copying current .claude data\"\n cp -a /root/.claude/. \"$CLAUDE_DATA/\"\n _log \"Copied .claude data to persistent storage\"\nfi\n\n# ── Migrate Claude binaries on first run ──────────────────────────────────────────────────────\nif [[ ! -L /root/.local/share/claude && -d /root/.local/share/claude ]]; then\n _warn \"First run — migrating Claude binaries → $CLAUDE_BIN\"\n cp -a /root/.local/share/claude/. \"$CLAUDE_BIN/\"\n _log \"Migrated Claude binaries\"\nfi\n\n# ── Create symlinks ───────────────────────────────────────────────────────────────────────────\n# Remove any real directories first — ln -sfn silently creates inside a dir instead of\n# replacing it, which produces a circular symlink on subsequent runs after migration.\nmkdir -p /root/.local/share /root/.local/bin\n\n[[ -d /root/.claude && ! -L /root/.claude ]] && rm -rf /root/.claude\nln -sfn \"$CLAUDE_DATA\" /root/.claude\n_log \".claude → $CLAUDE_DATA\"\n\n[[ -d /root/.local/share/claude && ! -L /root/.local/share/claude ]] && rm -rf /root/.local/share/claude\nln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude\n_log \"claude binary → $CLAUDE_BIN\"\n\n# ── Point the claude binary at the latest installed version ───────────────────────────────────\nLATEST=$(ls \"$CLAUDE_BIN/versions/\" 2>/dev/null | sort -V | tail -1)\nif [[ -z \"$LATEST\" ]]; then\n _err \"No Claude versions found in $CLAUDE_BIN/versions/\"\n _err \"Install Claude Code first: npm install -g @anthropic-ai/claude-code\"\n exit 1\nfi\n\nln -sfn \"$CLAUDE_BIN/versions/$LATEST\" /root/.local/bin/claude\n_log \"claude v$LATEST ready\"\n\necho \"\"\n\n# ── Setup-only mode (used by array_started.sh or other callers) ─────────────────────────────────\nif [[ \"$LAUNCH\" == false ]]; then\n _log \"Setup complete — run 'claude' to start\"\n echo \"\"\n exit 0\nfi\n\n# ── Launch ────────────────────────────────────────────────────────────────────────────────────\nexport PATH=\"$HOME/.local/bin:$PATH\"\nexec claude\n","structuredPatch":[{"oldStart":87,"oldLines":6,"newStart":87,"newLines":17,"lines":[" ln -sfn \"$CLAUDE_BIN\" /root/.local/share/claude"," _log \"claude binary → $CLAUDE_BIN\""," ","+# ── Symlink CLAUDE.md ─────────────────────────────────────────────────────────────────────────","+# Lives on /boot so it survives reboots without appdata. Symlinked into /root so Claude","+# picks it up automatically from the working directory on every session.","+CLAUDE_MD=\"/boot/config/plugins/varaverk/CLAUDE.md\"","+if [[ -f \"$CLAUDE_MD\" ]]; then","+ ln -sfn \"$CLAUDE_MD\" /root/CLAUDE.md","+ _log \"CLAUDE.md → $CLAUDE_MD\"","+else","+ _warn \"CLAUDE.md not found at $CLAUDE_MD — skipping symlink\"","+fi","+"," # ── Point the claude binary at the latest installed version ───────────────────────────────────"," LATEST=$(ls \"$CLAUDE_BIN/versions/\" 2>/dev/null | sort -V | tail -1)"," if [[ -z \"$LATEST\" ]]; then"]}],"userModified":false,"replaceAll":false},"sourceToolAssistantUUID":"d57bcc7f-011d-440e-9a07-5211de1de929","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"d5d11201-b561-45bc-b9bd-0ff11d308c2e","isSidechain":false,"attachment":{"type":"task_reminder","content":[],"itemCount":0},"type":"attachment","uuid":"fda0530d-f1cc-4ee0-aa54-0bdf2769a5f0","timestamp":"2026-06-06T17:54:49.645Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"last-prompt","lastPrompt":"yes, do that. and lets make sure our claud script loads it on an unraid reboot","leafUuid":"fda0530d-f1cc-4ee0-aa54-0bdf2769a5f0","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"ai-title","aiTitle":"Troubleshoot NPM authentication failure","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"mode","mode":"normal","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"type":"permission-mode","permissionMode":"acceptEdits","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c"} {"parentUuid":"fda0530d-f1cc-4ee0-aa54-0bdf2769a5f0","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01QHfkBBHLX6u1eu8KQCo8ee","type":"message","role":"assistant","content":[{"type":"text","text":"Now create the symlink for the current session too (don't wait for next reboot)."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":541,"cache_read_input_tokens":92127,"output_tokens":134,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":541,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":134,"cache_read_input_tokens":92127,"cache_creation_input_tokens":541,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":541},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSCofmjAPnFD7TGtcAb","type":"assistant","uuid":"c3421b03-4f2f-4f14-83b3-24cde71d6ebd","timestamp":"2026-06-06T17:54:51.984Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"c3421b03-4f2f-4f14-83b3-24cde71d6ebd","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01QHfkBBHLX6u1eu8KQCo8ee","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01D1SnEFjocMZimop42oRyWv","name":"Bash","input":{"command":"ln -sfn /boot/config/plugins/varaverk/CLAUDE.md /root/CLAUDE.md && ls -la /root/CLAUDE.md","description":"Create CLAUDE.md symlink for current session"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":541,"cache_read_input_tokens":92127,"output_tokens":134,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":541,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":134,"cache_read_input_tokens":92127,"cache_creation_input_tokens":541,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":541},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSCofmjAPnFD7TGtcAb","type":"assistant","uuid":"59aa055a-74e4-445a-b56a-2a9a0da3e3e9","timestamp":"2026-06-06T17:54:52.601Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"59aa055a-74e4-445a-b56a-2a9a0da3e3e9","isSidechain":false,"promptId":"62460e8f-473b-4e84-aea9-2a59d7686fd3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01D1SnEFjocMZimop42oRyWv","type":"tool_result","content":"lrwxrwxrwx 1 root root 39 Jun 6 13:54 /root/CLAUDE.md -> /boot/config/plugins/varaverk/CLAUDE.md","is_error":false}]},"uuid":"2dd32ae4-73ae-4b57-b6cc-cd604fec397d","timestamp":"2026-06-06T17:54:58.746Z","toolUseResult":{"stdout":"lrwxrwxrwx 1 root root 39 Jun 6 13:54 /root/CLAUDE.md -> /boot/config/plugins/varaverk/CLAUDE.md","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"59aa055a-74e4-445a-b56a-2a9a0da3e3e9","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"2dd32ae4-73ae-4b57-b6cc-cd604fec397d","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01CvENH9GWoCvbepQdyYhCjC","type":"message","role":"assistant","content":[{"type":"text","text":"Done. Here's what was built:\n\n**`/boot/config/plugins/varaverk/CLAUDE.md`** — project context that covers:\n- Working rules (right workspace, no Co-Authored-By)\n- Two-server architecture and host identities\n- The three-file conf system and how `detect_hosts()` works\n- Full orchestrator schedule\n- Current rsync toggle state\n- Fallback system rules\n- Known gaps (the HOST1 NPM credentials gap you just hit)\n- How Claude persistence works on Unraid\n\n**`Tools/claude_startup.sh`** — now also symlinks `/root/CLAUDE.md` → the CLAUDE.md on `/boot` every time the array starts, so it survives reboots automatically.\n\nThe symlink is live now for this session. Every future Claude session starts with all that context already loaded — no more losing a quarter of the conversation."}],"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":195,"cache_read_input_tokens":92668,"output_tokens":209,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":195,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":1,"output_tokens":209,"cache_read_input_tokens":92668,"cache_creation_input_tokens":195,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":195},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSDUZ7Eeuqjus9Q3cdn","type":"assistant","uuid":"fb108e01-4b0a-42da-9150-dc8f2975e3ea","timestamp":"2026-06-06T17:55:05.883Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"fb108e01-4b0a-42da-9150-dc8f2975e3ea","isSidechain":false,"type":"system","subtype":"turn_duration","durationMs":98590,"messageCount":56,"timestamp":"2026-06-06T17:55:05.964Z","uuid":"435a7d08-97be-4b15-97dc-4d92fd46f05a","isMeta":false,"userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"type":"file-history-snapshot","messageId":"af542a6a-c159-4bcd-b8f5-f96fe3505d58","snapshot":{"messageId":"af542a6a-c159-4bcd-b8f5-f96fe3505d58","trackedFileBackups":{"/boot/config/plugins/varaverk/CLAUDE.md":{"backupFileName":"489673a52f706ddf@v2","version":2,"backupTime":"2026-06-06T17:55:42.131Z"},"/boot/config/plugins/varaverk/Tools/claude_startup.sh":{"backupFileName":"f4b792a0d6c7bbfd@v2","version":2,"backupTime":"2026-06-06T17:55:42.133Z"}},"timestamp":"2026-06-06T17:55:42.133Z"},"isSnapshotUpdate":false} {"parentUuid":"435a7d08-97be-4b15-97dc-4d92fd46f05a","isSidechain":false,"promptId":"c33608d3-1ae8-44c1-9f5c-de9888148419","type":"user","message":{"role":"user","content":"ok so lets fix the auth page and master and hosts.conf..."},"uuid":"af542a6a-c159-4bcd-b8f5-f96fe3505d58","timestamp":"2026-06-06T17:55:42.106Z","permissionMode":"acceptEdits","promptSource":"typed","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"af542a6a-c159-4bcd-b8f5-f96fe3505d58","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01Xg4nQWEQMaUV9fXqHaQnJk","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"Es4ECmUIDhgCKkC17x3nGUz37Wu5q2YVtoaoMd+aLHRKhvRSWT+5zGMrH0ZP+YaKbi1WTVxRiBrEmhII1zkvvfGevU0yHddtXeKjMhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIMmbiUzH2LZGCQB8vjGgyNVZJZtyxSlyeq3hciMHZqifjUnb3Gf43ynsk6l3EAJtbqHBY7j8HDK0z03kRQjVr7GnJhHzcaq/e9CSElUSqWA4sMLCm/SPiX8q0MVKhlwmsC8C8gOT/a7UEXR074CGzMRMRewOWOroujc6w7gr6+XOnXyMYNRxOSgXLAFH2CaI6qF/CXr878dhcVbpT+JWt+v6UO5CKa65D4fN8SSezlqeRuZVj67h/0gJ3FOQEkBnn0jyy2lUG0gxL64YY0JxrUfJR1eoUrpVkOjx3sCr7d406HI6uFPJxjToo42bgAGPGZ2jUoMa3SnylKOysXPcMydOteZIxFbQ46MZWuG206WzuRPds5oY2Pi+76SqjdkHPWdXQc+DoJyKVdfcc7S/O/CplJNiByZk2jYfi1GwTqW0Hc6L9dmMiNRu0L+rrhCQbhI0rSj8bripRcpfJUI+YK9F5D6ZEspf11/dbkmW8qc1wfVwM/w3ZyrbPuR+RQqgiAzC1XYHREn2GVeebPPzA1Hbb3zC97GpgEZddC6nVT01KXd9CcU9F07h10O6qKNrbaJpLds5bnkn319lrJlz/QB2q3KAfMGOYIDNno0bKkvEnEUiFNrY4bijr6ufp9m4OXYZi78zIYAQ=="}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":17,"cache_read_input_tokens":93070,"output_tokens":260,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":17,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":260,"cache_read_input_tokens":93070,"cache_creation_input_tokens":17,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":17},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSGgBBmhmZgywWmSYdQ","type":"assistant","uuid":"1debc8c4-29c2-4e39-a30c-02e1e1fef556","timestamp":"2026-06-06T17:55:46.263Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"1debc8c4-29c2-4e39-a30c-02e1e1fef556","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01Xg4nQWEQMaUV9fXqHaQnJk","type":"message","role":"assistant","content":[{"type":"text","text":"Let me read the auth files to see the full picture before touching anything."}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":17,"cache_read_input_tokens":93070,"output_tokens":260,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":17,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":260,"cache_read_input_tokens":93070,"cache_creation_input_tokens":17,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":17},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSGgBBmhmZgywWmSYdQ","type":"assistant","uuid":"9666bc4c-a93d-459f-9bad-20210f2ca201","timestamp":"2026-06-06T17:55:46.688Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"9666bc4c-a93d-459f-9bad-20210f2ca201","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01Xg4nQWEQMaUV9fXqHaQnJk","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Rq9LM3T3rX5pzMNz3get5T","name":"Read","input":{"file_path":"/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":17,"cache_read_input_tokens":93070,"output_tokens":260,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":17,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":260,"cache_read_input_tokens":93070,"cache_creation_input_tokens":17,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":17},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSGgBBmhmZgywWmSYdQ","type":"assistant","uuid":"5162f405-4ed2-4dd4-b407-195e85d06ba8","timestamp":"2026-06-06T17:55:47.150Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"5162f405-4ed2-4dd4-b407-195e85d06ba8","isSidechain":false,"promptId":"c33608d3-1ae8-44c1-9f5c-de9888148419","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Rq9LM3T3rX5pzMNz3get5T","type":"tool_result","content":"1\t rtrim($v[\"{$host}_NPM_URL\"] ?? 'http://localhost:81', '/'),\n11\t 'npm_user' => $v[\"{$host}_NPM_USER\"] ?? '',\n12\t 'npm_pass' => $v[\"{$host}_NPM_PASS\"] ?? '',\n13\t 'lldap_url' => rtrim($v[\"{$host}_LLDAP_URL\"] ?? 'http://localhost:17170', '/'),\n14\t 'lldap_user' => $v[\"{$host}_LLDAP_USER\"] ?? '',\n15\t 'lldap_pass' => $v[\"{$host}_LLDAP_PASS\"] ?? '',\n16\t 'authelia_config' => $v[\"{$host}_AUTHELIA_CONFIG\"] ?? '/mnt/user/appdata-Fallback/Critical-Data/Authelia/configuration.yml',\n17\t 'authelia_container' => $v[\"{$host}_AUTHELIA_CONTAINER\"] ?? 'Authelia',\n18\t 'is_owner' => vv_is_owner(),\n19\t ];\n20\t}\n21\t\n22\t// ── NPM ───────────────────────────────────────────────────────────────────────\n23\t\n24\tfunction vv_npm_token(): string {\n25\t if (!session_id()) session_start();\n26\t $conf = vv_auth_conf();\n27\t $cached = $_SESSION['vv_npm_token'] ?? '';\n28\t $expiry = $_SESSION['vv_npm_token_exp'] ?? 0;\n29\t if ($cached && time() < $expiry) return $cached;\n30\t\n31\t $resp = vv_npm_raw('POST', '/api/tokens', [\n32\t 'identity' => $conf['npm_user'],\n33\t 'secret' => $conf['npm_pass'],\n34\t 'expiry' => '1d',\n35\t ], '', $conf);\n36\t $token = $resp['token'] ?? '';\n37\t if ($token) {\n38\t $_SESSION['vv_npm_token'] = $token;\n39\t $_SESSION['vv_npm_token_exp'] = time() + 82800;\n40\t }\n41\t return $token;\n42\t}\n43\t\n44\tfunction vv_npm_raw(string $method, string $path, array $data, string $token, array $conf = []): array {\n45\t if (!$conf) $conf = vv_auth_conf();\n46\t $url = $conf['npm_url'] . $path;\n47\t $headers = ['Content-Type: application/json', 'Accept: application/json'];\n48\t if ($token) $headers[] = 'Authorization: Bearer ' . $token;\n49\t\n50\t $ch = curl_init($url);\n51\t curl_setopt_array($ch, [\n52\t CURLOPT_RETURNTRANSFER => true,\n53\t CURLOPT_TIMEOUT => 10,\n54\t CURLOPT_HTTPHEADER => $headers,\n55\t CURLOPT_CUSTOMREQUEST => $method,\n56\t ]);\n57\t if ($data && in_array($method, ['POST', 'PUT'], true))\n58\t curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n59\t $body = curl_exec($ch);\n60\t curl_close($ch);\n61\t return json_decode($body ?: '{}', true) ?: [];\n62\t}\n63\t\n64\tfunction vv_npm_req(string $method, string $path, array $data = []): array {\n65\t $token = vv_npm_token();\n66\t if (!$token) return ['_err' => 'NPM auth failed — check credentials in host conf'];\n67\t return vv_npm_raw($method, $path, $data, $token);\n68\t}\n69\t\n70\tfunction vv_npm_list_proxies(): array {\n71\t $list = vv_npm_req('GET', '/api/nginx/proxy-hosts?expand=certificate');\n72\t if (!is_array($list) || isset($list['_err']))\n73\t return ['ok' => false, 'error' => $list['_err'] ?? 'Invalid response from NPM'];\n74\t return ['ok' => true, 'proxies' => $list];\n75\t}\n76\t\n77\tfunction vv_npm_list_certs(): array {\n78\t $list = vv_npm_req('GET', '/api/nginx/certificates');\n79\t return is_array($list) ? $list : [];\n80\t}\n81\t\n82\tfunction vv_npm_create_proxy(array $data): array {\n83\t $r = vv_npm_req('POST', '/api/nginx/proxy-hosts', $data);\n84\t return isset($r['id']) ? ['ok' => true, 'proxy' => $r] : ['ok' => false, 'error' => $r['error'] ?? ($r['_err'] ?? 'Create failed')];\n85\t}\n86\t\n87\tfunction vv_npm_update_proxy(int $id, array $data): array {\n88\t $r = vv_npm_req('PUT', \"/api/nginx/proxy-hosts/$id\", $data);\n89\t return isset($r['id']) ? ['ok' => true, 'proxy' => $r] : ['ok' => false, 'error' => $r['error'] ?? ($r['_err'] ?? 'Update failed')];\n90\t}\n91\t\n92\tfunction vv_npm_delete_proxy(int $id): array {\n93\t vv_npm_req('DELETE', \"/api/nginx/proxy-hosts/$id\");\n94\t return ['ok' => true];\n95\t}\n96\t\n97\tfunction vv_npm_toggle_proxy(int $id, bool $enabled): array {\n98\t vv_npm_req('POST', \"/api/nginx/proxy-hosts/$id/\" . ($enabled ? 'enable' : 'disable'));\n99\t return ['ok' => true];\n100\t}\n101\t\n102\t// ── lldap ─────────────────────────────────────────────────────────────────────\n103\t\n104\tfunction vv_lldap_token(): string {\n105\t if (!session_id()) session_start();\n106\t $conf = vv_auth_conf();\n107\t $cached = $_SESSION['vv_lldap_token'] ?? '';\n108\t $expiry = $_SESSION['vv_lldap_token_exp'] ?? 0;\n109\t if ($cached && time() < $expiry) return $cached;\n110\t\n111\t $ch = curl_init($conf['lldap_url'] . '/auth/simple/login');\n112\t curl_setopt_array($ch, [\n113\t CURLOPT_RETURNTRANSFER => true,\n114\t CURLOPT_TIMEOUT => 10,\n115\t CURLOPT_POST => true,\n116\t CURLOPT_POSTFIELDS => json_encode(['username' => $conf['lldap_user'], 'password' => $conf['lldap_pass']]),\n117\t CURLOPT_HTTPHEADER => ['Content-Type: application/json'],\n118\t ]);\n119\t $body = curl_exec($ch);\n120\t curl_close($ch);\n121\t $resp = json_decode($body ?: '{}', true) ?: [];\n122\t $token = $resp['token'] ?? '';\n123\t if ($token) {\n124\t $_SESSION['vv_lldap_token'] = $token;\n125\t $_SESSION['vv_lldap_token_exp'] = time() + 3500;\n126\t }\n127\t return $token;\n128\t}\n129\t\n130\tfunction vv_lldap_gql(string $query, array $variables = []): array {\n131\t $conf = vv_auth_conf();\n132\t $token = vv_lldap_token();\n133\t if (!$token) return ['errors' => [['message' => 'lldap auth failed — check credentials']]];\n134\t\n135\t $ch = curl_init($conf['lldap_url'] . '/api/graphql');\n136\t curl_setopt_array($ch, [\n137\t CURLOPT_RETURNTRANSFER => true,\n138\t CURLOPT_TIMEOUT => 10,\n139\t CURLOPT_POST => true,\n140\t CURLOPT_POSTFIELDS => json_encode(['query' => $query, 'variables' => $variables]),\n141\t CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $token],\n142\t ]);\n143\t $body = curl_exec($ch);\n144\t curl_close($ch);\n145\t return json_decode($body ?: '{}', true) ?: [];\n146\t}\n147\t\n148\tfunction vv_lldap_list_users(): array {\n149\t $r = vv_lldap_gql('query { listUsers { id displayName email creationDate groups { id displayName } } }');\n150\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Query failed'];\n151\t return ['ok' => true, 'users' => $r['data']['listUsers'] ?? []];\n152\t}\n153\t\n154\tfunction vv_lldap_list_groups(): array {\n155\t $r = vv_lldap_gql('query { listGroups { id displayName users { id displayName } } }');\n156\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Query failed'];\n157\t return ['ok' => true, 'groups' => $r['data']['listGroups'] ?? []];\n158\t}\n159\t\n160\tfunction vv_lldap_create_user(string $id, string $email, string $displayName, string $password): array {\n161\t $r = vv_lldap_gql(\n162\t 'mutation CreateUser($user: CreateUserInput!) { createUser(user: $user) { id displayName email } }',\n163\t ['user' => ['id' => $id, 'email' => $email, 'displayName' => $displayName]]\n164\t );\n165\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Create failed'];\n166\t if ($password) vv_lldap_set_password($id, $password);\n167\t return ['ok' => true, 'user' => $r['data']['createUser'] ?? []];\n168\t}\n169\t\n170\tfunction vv_lldap_update_user(string $id, string $email, string $displayName): array {\n171\t $r = vv_lldap_gql(\n172\t 'mutation UpdateUser($user: UpdateUserInput!) { updateUser(user: $user) { ok } }',\n173\t ['user' => ['id' => $id, 'email' => $email, 'displayName' => $displayName]]\n174\t );\n175\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Update failed'];\n176\t return ['ok' => true];\n177\t}\n178\t\n179\tfunction vv_lldap_delete_user(string $id): array {\n180\t $r = vv_lldap_gql(\n181\t 'mutation DeleteUser($userId: String!) { deleteUser(userId: $userId) { ok } }',\n182\t ['userId' => $id]\n183\t );\n184\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Delete failed'];\n185\t return ['ok' => true];\n186\t}\n187\t\n188\tfunction vv_lldap_set_password(string $userId, string $password): array {\n189\t $r = vv_lldap_gql(\n190\t 'mutation ChangePassword($userId: String!, $password: String!) { changeUserPassword(userId: $userId, password: $password) }',\n191\t ['userId' => $userId, 'password' => $password]\n192\t );\n193\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Password change failed'];\n194\t return ['ok' => true];\n195\t}\n196\t\n197\tfunction vv_lldap_create_group(string $name): array {\n198\t $r = vv_lldap_gql(\n199\t 'mutation CreateGroup($name: String!) { createGroup(name: $name) { id displayName } }',\n200\t ['name' => $name]\n201\t );\n202\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Create failed'];\n203\t return ['ok' => true, 'group' => $r['data']['createGroup'] ?? []];\n204\t}\n205\t\n206\tfunction vv_lldap_delete_group(int $id): array {\n207\t $r = vv_lldap_gql(\n208\t 'mutation DeleteGroup($groupId: Int!) { deleteGroup(groupId: $groupId) { ok } }',\n209\t ['groupId' => $id]\n210\t );\n211\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Delete failed'];\n212\t return ['ok' => true];\n213\t}\n214\t\n215\tfunction vv_lldap_add_to_group(string $userId, int $groupId): array {\n216\t $r = vv_lldap_gql(\n217\t 'mutation AddUserToGroup($userId: String!, $groupId: Int!) { addUserToGroup(userId: $userId, groupId: $groupId) { ok } }',\n218\t ['userId' => $userId, 'groupId' => $groupId]\n219\t );\n220\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Failed'];\n221\t return ['ok' => true];\n222\t}\n223\t\n224\tfunction vv_lldap_remove_from_group(string $userId, int $groupId): array {\n225\t $r = vv_lldap_gql(\n226\t 'mutation RemoveUserFromGroup($userId: String!, $groupId: Int!) { removeUserFromGroup(userId: $userId, groupId: $groupId) { ok } }',\n227\t ['userId' => $userId, 'groupId' => $groupId]\n228\t );\n229\t if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Failed'];\n230\t return ['ok' => true];\n231\t}\n232\t\n233\t// ── Authelia ──────────────────────────────────────────────────────────────────\n234\t\n235\tfunction vv_authelia_read_rules(): array {\n236\t $conf = vv_auth_conf();\n237\t $file = $conf['authelia_config'];\n238\t if (!file_exists($file)) return ['ok' => false, 'error' => 'Config not found: ' . $file];\n239\t\n240\t $py = \"import yaml,json,sys\\n\"\n241\t . \"d=yaml.safe_load(open(sys.argv[1]))\\n\"\n242\t . \"ac=d.get('access_control',{})\\n\"\n243\t . \"print(json.dumps({'default_policy':ac.get('default_policy','deny'),'rules':ac.get('rules',[])}))\\n\";\n244\t $tmp = '/tmp/vv_auth_rd_' . getmypid() . '.py';\n245\t file_put_contents($tmp, $py);\n246\t $out = shell_exec('python3 ' . escapeshellarg($tmp) . ' ' . escapeshellarg($file) . ' 2>/dev/null');\n247\t @unlink($tmp);\n248\t\n249\t if (!$out) return ['ok' => false, 'error' => 'Parse failed — python3 with PyYAML required'];\n250\t $data = json_decode(trim($out), true);\n251\t if (!$data) return ['ok' => false, 'error' => 'Invalid YAML response'];\n252\t return ['ok' => true, 'default_policy' => $data['default_policy'], 'rules' => $data['rules']];\n253\t}\n254\t\n255\tfunction vv_authelia_write_rules(array $rules, string $defaultPolicy): array {\n256\t $conf = vv_auth_conf();\n257\t $file = $conf['authelia_config'];\n258\t if (!file_exists($file)) return ['ok' => false, 'error' => 'Config not found: ' . $file];\n259\t\n260\t $acJson = json_encode(['default_policy' => $defaultPolicy, 'rules' => $rules]);\n261\t $py = <<<'PYEOF'\n262\timport yaml, json, sys, re\n263\tconfig_file = sys.argv[1]\n264\tnew_ac = json.loads(sys.argv[2])\n265\twith open(config_file, 'r') as f:\n266\t content = f.read()\n267\tnew_block = yaml.dump({'access_control': new_ac}, default_flow_style=False, allow_unicode=True, sort_keys=False)\n268\tpattern = r'(?ms)^access_control:.*?(?=^[a-zA-Z#]|\\Z)'\n269\tif re.search(pattern, content):\n270\t content = re.sub(pattern, new_block + '\\n', content)\n271\telse:\n272\t content = content.rstrip('\\n') + '\\n\\n' + new_block + '\\n'\n273\twith open(config_file, 'w') as f:\n274\t f.write(content)\n275\tprint('ok')\n276\tPYEOF;\n277\t $tmp = '/tmp/vv_auth_wr_' . getmypid() . '.py';\n278\t file_put_contents($tmp, $py);\n279\t $out = shell_exec('python3 ' . escapeshellarg($tmp) . ' ' . escapeshellarg($file) . ' ' . escapeshellarg($acJson) . ' 2>&1');\n280\t @unlink($tmp);\n281\t\n282\t if (trim($out) !== 'ok') return ['ok' => false, 'error' => 'Write failed: ' . trim($out)];\n283\t shell_exec('docker restart ' . escapeshellarg($conf['authelia_container']) . ' >/dev/null 2>&1 &');\n284\t return ['ok' => true];\n285\t}\n286\t"}]},"uuid":"8908fcfe-4b75-493f-8c15-6aa3bb8c0c8b","timestamp":"2026-06-06T17:55:47.173Z","toolUseResult":{"type":"text","file":{"filePath":"/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php","content":" rtrim($v[\"{$host}_NPM_URL\"] ?? 'http://localhost:81', '/'),\n 'npm_user' => $v[\"{$host}_NPM_USER\"] ?? '',\n 'npm_pass' => $v[\"{$host}_NPM_PASS\"] ?? '',\n 'lldap_url' => rtrim($v[\"{$host}_LLDAP_URL\"] ?? 'http://localhost:17170', '/'),\n 'lldap_user' => $v[\"{$host}_LLDAP_USER\"] ?? '',\n 'lldap_pass' => $v[\"{$host}_LLDAP_PASS\"] ?? '',\n 'authelia_config' => $v[\"{$host}_AUTHELIA_CONFIG\"] ?? '/mnt/user/appdata-Fallback/Critical-Data/Authelia/configuration.yml',\n 'authelia_container' => $v[\"{$host}_AUTHELIA_CONTAINER\"] ?? 'Authelia',\n 'is_owner' => vv_is_owner(),\n ];\n}\n\n// ── NPM ───────────────────────────────────────────────────────────────────────\n\nfunction vv_npm_token(): string {\n if (!session_id()) session_start();\n $conf = vv_auth_conf();\n $cached = $_SESSION['vv_npm_token'] ?? '';\n $expiry = $_SESSION['vv_npm_token_exp'] ?? 0;\n if ($cached && time() < $expiry) return $cached;\n\n $resp = vv_npm_raw('POST', '/api/tokens', [\n 'identity' => $conf['npm_user'],\n 'secret' => $conf['npm_pass'],\n 'expiry' => '1d',\n ], '', $conf);\n $token = $resp['token'] ?? '';\n if ($token) {\n $_SESSION['vv_npm_token'] = $token;\n $_SESSION['vv_npm_token_exp'] = time() + 82800;\n }\n return $token;\n}\n\nfunction vv_npm_raw(string $method, string $path, array $data, string $token, array $conf = []): array {\n if (!$conf) $conf = vv_auth_conf();\n $url = $conf['npm_url'] . $path;\n $headers = ['Content-Type: application/json', 'Accept: application/json'];\n if ($token) $headers[] = 'Authorization: Bearer ' . $token;\n\n $ch = curl_init($url);\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_TIMEOUT => 10,\n CURLOPT_HTTPHEADER => $headers,\n CURLOPT_CUSTOMREQUEST => $method,\n ]);\n if ($data && in_array($method, ['POST', 'PUT'], true))\n curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));\n $body = curl_exec($ch);\n curl_close($ch);\n return json_decode($body ?: '{}', true) ?: [];\n}\n\nfunction vv_npm_req(string $method, string $path, array $data = []): array {\n $token = vv_npm_token();\n if (!$token) return ['_err' => 'NPM auth failed — check credentials in host conf'];\n return vv_npm_raw($method, $path, $data, $token);\n}\n\nfunction vv_npm_list_proxies(): array {\n $list = vv_npm_req('GET', '/api/nginx/proxy-hosts?expand=certificate');\n if (!is_array($list) || isset($list['_err']))\n return ['ok' => false, 'error' => $list['_err'] ?? 'Invalid response from NPM'];\n return ['ok' => true, 'proxies' => $list];\n}\n\nfunction vv_npm_list_certs(): array {\n $list = vv_npm_req('GET', '/api/nginx/certificates');\n return is_array($list) ? $list : [];\n}\n\nfunction vv_npm_create_proxy(array $data): array {\n $r = vv_npm_req('POST', '/api/nginx/proxy-hosts', $data);\n return isset($r['id']) ? ['ok' => true, 'proxy' => $r] : ['ok' => false, 'error' => $r['error'] ?? ($r['_err'] ?? 'Create failed')];\n}\n\nfunction vv_npm_update_proxy(int $id, array $data): array {\n $r = vv_npm_req('PUT', \"/api/nginx/proxy-hosts/$id\", $data);\n return isset($r['id']) ? ['ok' => true, 'proxy' => $r] : ['ok' => false, 'error' => $r['error'] ?? ($r['_err'] ?? 'Update failed')];\n}\n\nfunction vv_npm_delete_proxy(int $id): array {\n vv_npm_req('DELETE', \"/api/nginx/proxy-hosts/$id\");\n return ['ok' => true];\n}\n\nfunction vv_npm_toggle_proxy(int $id, bool $enabled): array {\n vv_npm_req('POST', \"/api/nginx/proxy-hosts/$id/\" . ($enabled ? 'enable' : 'disable'));\n return ['ok' => true];\n}\n\n// ── lldap ─────────────────────────────────────────────────────────────────────\n\nfunction vv_lldap_token(): string {\n if (!session_id()) session_start();\n $conf = vv_auth_conf();\n $cached = $_SESSION['vv_lldap_token'] ?? '';\n $expiry = $_SESSION['vv_lldap_token_exp'] ?? 0;\n if ($cached && time() < $expiry) return $cached;\n\n $ch = curl_init($conf['lldap_url'] . '/auth/simple/login');\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_TIMEOUT => 10,\n CURLOPT_POST => true,\n CURLOPT_POSTFIELDS => json_encode(['username' => $conf['lldap_user'], 'password' => $conf['lldap_pass']]),\n CURLOPT_HTTPHEADER => ['Content-Type: application/json'],\n ]);\n $body = curl_exec($ch);\n curl_close($ch);\n $resp = json_decode($body ?: '{}', true) ?: [];\n $token = $resp['token'] ?? '';\n if ($token) {\n $_SESSION['vv_lldap_token'] = $token;\n $_SESSION['vv_lldap_token_exp'] = time() + 3500;\n }\n return $token;\n}\n\nfunction vv_lldap_gql(string $query, array $variables = []): array {\n $conf = vv_auth_conf();\n $token = vv_lldap_token();\n if (!$token) return ['errors' => [['message' => 'lldap auth failed — check credentials']]];\n\n $ch = curl_init($conf['lldap_url'] . '/api/graphql');\n curl_setopt_array($ch, [\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_TIMEOUT => 10,\n CURLOPT_POST => true,\n CURLOPT_POSTFIELDS => json_encode(['query' => $query, 'variables' => $variables]),\n CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $token],\n ]);\n $body = curl_exec($ch);\n curl_close($ch);\n return json_decode($body ?: '{}', true) ?: [];\n}\n\nfunction vv_lldap_list_users(): array {\n $r = vv_lldap_gql('query { listUsers { id displayName email creationDate groups { id displayName } } }');\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Query failed'];\n return ['ok' => true, 'users' => $r['data']['listUsers'] ?? []];\n}\n\nfunction vv_lldap_list_groups(): array {\n $r = vv_lldap_gql('query { listGroups { id displayName users { id displayName } } }');\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Query failed'];\n return ['ok' => true, 'groups' => $r['data']['listGroups'] ?? []];\n}\n\nfunction vv_lldap_create_user(string $id, string $email, string $displayName, string $password): array {\n $r = vv_lldap_gql(\n 'mutation CreateUser($user: CreateUserInput!) { createUser(user: $user) { id displayName email } }',\n ['user' => ['id' => $id, 'email' => $email, 'displayName' => $displayName]]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Create failed'];\n if ($password) vv_lldap_set_password($id, $password);\n return ['ok' => true, 'user' => $r['data']['createUser'] ?? []];\n}\n\nfunction vv_lldap_update_user(string $id, string $email, string $displayName): array {\n $r = vv_lldap_gql(\n 'mutation UpdateUser($user: UpdateUserInput!) { updateUser(user: $user) { ok } }',\n ['user' => ['id' => $id, 'email' => $email, 'displayName' => $displayName]]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Update failed'];\n return ['ok' => true];\n}\n\nfunction vv_lldap_delete_user(string $id): array {\n $r = vv_lldap_gql(\n 'mutation DeleteUser($userId: String!) { deleteUser(userId: $userId) { ok } }',\n ['userId' => $id]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Delete failed'];\n return ['ok' => true];\n}\n\nfunction vv_lldap_set_password(string $userId, string $password): array {\n $r = vv_lldap_gql(\n 'mutation ChangePassword($userId: String!, $password: String!) { changeUserPassword(userId: $userId, password: $password) }',\n ['userId' => $userId, 'password' => $password]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Password change failed'];\n return ['ok' => true];\n}\n\nfunction vv_lldap_create_group(string $name): array {\n $r = vv_lldap_gql(\n 'mutation CreateGroup($name: String!) { createGroup(name: $name) { id displayName } }',\n ['name' => $name]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Create failed'];\n return ['ok' => true, 'group' => $r['data']['createGroup'] ?? []];\n}\n\nfunction vv_lldap_delete_group(int $id): array {\n $r = vv_lldap_gql(\n 'mutation DeleteGroup($groupId: Int!) { deleteGroup(groupId: $groupId) { ok } }',\n ['groupId' => $id]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Delete failed'];\n return ['ok' => true];\n}\n\nfunction vv_lldap_add_to_group(string $userId, int $groupId): array {\n $r = vv_lldap_gql(\n 'mutation AddUserToGroup($userId: String!, $groupId: Int!) { addUserToGroup(userId: $userId, groupId: $groupId) { ok } }',\n ['userId' => $userId, 'groupId' => $groupId]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Failed'];\n return ['ok' => true];\n}\n\nfunction vv_lldap_remove_from_group(string $userId, int $groupId): array {\n $r = vv_lldap_gql(\n 'mutation RemoveUserFromGroup($userId: String!, $groupId: Int!) { removeUserFromGroup(userId: $userId, groupId: $groupId) { ok } }',\n ['userId' => $userId, 'groupId' => $groupId]\n );\n if (isset($r['errors'])) return ['ok' => false, 'error' => $r['errors'][0]['message'] ?? 'Failed'];\n return ['ok' => true];\n}\n\n// ── Authelia ──────────────────────────────────────────────────────────────────\n\nfunction vv_authelia_read_rules(): array {\n $conf = vv_auth_conf();\n $file = $conf['authelia_config'];\n if (!file_exists($file)) return ['ok' => false, 'error' => 'Config not found: ' . $file];\n\n $py = \"import yaml,json,sys\\n\"\n . \"d=yaml.safe_load(open(sys.argv[1]))\\n\"\n . \"ac=d.get('access_control',{})\\n\"\n . \"print(json.dumps({'default_policy':ac.get('default_policy','deny'),'rules':ac.get('rules',[])}))\\n\";\n $tmp = '/tmp/vv_auth_rd_' . getmypid() . '.py';\n file_put_contents($tmp, $py);\n $out = shell_exec('python3 ' . escapeshellarg($tmp) . ' ' . escapeshellarg($file) . ' 2>/dev/null');\n @unlink($tmp);\n\n if (!$out) return ['ok' => false, 'error' => 'Parse failed — python3 with PyYAML required'];\n $data = json_decode(trim($out), true);\n if (!$data) return ['ok' => false, 'error' => 'Invalid YAML response'];\n return ['ok' => true, 'default_policy' => $data['default_policy'], 'rules' => $data['rules']];\n}\n\nfunction vv_authelia_write_rules(array $rules, string $defaultPolicy): array {\n $conf = vv_auth_conf();\n $file = $conf['authelia_config'];\n if (!file_exists($file)) return ['ok' => false, 'error' => 'Config not found: ' . $file];\n\n $acJson = json_encode(['default_policy' => $defaultPolicy, 'rules' => $rules]);\n $py = <<<'PYEOF'\nimport yaml, json, sys, re\nconfig_file = sys.argv[1]\nnew_ac = json.loads(sys.argv[2])\nwith open(config_file, 'r') as f:\n content = f.read()\nnew_block = yaml.dump({'access_control': new_ac}, default_flow_style=False, allow_unicode=True, sort_keys=False)\npattern = r'(?ms)^access_control:.*?(?=^[a-zA-Z#]|\\Z)'\nif re.search(pattern, content):\n content = re.sub(pattern, new_block + '\\n', content)\nelse:\n content = content.rstrip('\\n') + '\\n\\n' + new_block + '\\n'\nwith open(config_file, 'w') as f:\n f.write(content)\nprint('ok')\nPYEOF;\n $tmp = '/tmp/vv_auth_wr_' . getmypid() . '.py';\n file_put_contents($tmp, $py);\n $out = shell_exec('python3 ' . escapeshellarg($tmp) . ' ' . escapeshellarg($file) . ' ' . escapeshellarg($acJson) . ' 2>&1');\n @unlink($tmp);\n\n if (trim($out) !== 'ok') return ['ok' => false, 'error' => 'Write failed: ' . trim($out)];\n shell_exec('docker restart ' . escapeshellarg($conf['authelia_container']) . ' >/dev/null 2>&1 &');\n return ['ok' => true];\n}\n","numLines":286,"startLine":1,"totalLines":286}},"sourceToolAssistantUUID":"5162f405-4ed2-4dd4-b407-195e85d06ba8","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"8908fcfe-4b75-493f-8c15-6aa3bb8c0c8b","isSidechain":false,"message":{"model":"claude-sonnet-4-6","id":"msg_01Xg4nQWEQMaUV9fXqHaQnJk","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01SHT86sbemfL2m8NdV5SruN","name":"Read","input":{"file_path":"/boot/config/plugins/varaverk/Plugin/unraid/pages/auth.php"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":17,"cache_read_input_tokens":93070,"output_tokens":260,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":17,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":260,"cache_read_input_tokens":93070,"cache_creation_input_tokens":17,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":17},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CbnSGgBBmhmZgywWmSYdQ","type":"assistant","uuid":"1e43a845-8aa1-440d-b453-002c9908091a","timestamp":"2026-06-06T17:55:47.634Z","userType":"external","entrypoint":"cli","cwd":"/root","sessionId":"d950f3bd-e034-44cb-a540-cdd63727516c","version":"2.1.167","gitBranch":"HEAD"} {"parentUuid":"1e43a845-8aa1-440d-b453-002c9908091a","isSidechain":false,"promptId":"c33608d3-1ae8-44c1-9f5c-de9888148419","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01SHT86sbemfL2m8NdV5SruN","type":"tool_result","content":"1\t\n121\t\n122\t\n126\t\n127\t
\n135\t\n136\t\n137\t