# Varaverk — Claude Code Context ## Working Rules (read first) - **Workspace is always** `/boot/config/plugins/varaverk` — every edit goes here. - **No Co-Authored-By** in commit messages unless explicitly asked. - **No comments** unless the WHY is genuinely non-obvious. - The `.plg` symlinks the installed plugin location directly to this workspace — one copy, no drift. ### Current workflow: edit here, push to Gitea Pre-release — too many moving parts for a dev/prod split right now. Edits are made directly in `/boot/config/plugins/varaverk/` and pushed. This is intentional, not a gap. ### Future: dev/prod split (post-release) When the codebase stabilises, the plan: - **Dev** — a separate git clone somewhere on the array (`/mnt/user/Development/Varaverk/` or similar). All editing happens there. - **Prod** — `/boot/config/plugins/varaverk/` remains as-is. Only updated via `git pull` (already handled by `git_pull_execute.sh` in the daily orchestrator, or triggered manually from the UI). - **`plugin_setup.sh`** stays pointing at the prod path. Dev never touches `/boot/` directly. - **Push is the only bridge** — no deploy hooks, no rsync-on-save, no direct path references between dev and prod. The old dev folder was deleted precisely because it violated this. ### Hard limits — do not cross these - **Never create or modify `.claude/settings.json`** in this repo. No workspace hooks, ever. The stale hook that existed here previously fired `Deployment/deploy.sh` (now deleted) on every file edit and caused unintended deploys. If you think a hook would help, ask first. - **Never change `HOST1_STORAGE_MODE_INTERNAL`** in `host1.conf`. Claude data belongs in `/mnt/user/appdata/claude-code/` — not inside this repo. - **There is no dev folder.** The old `/mnt/user/Important Shit/Git/Development/Varaverk` copy was deleted. Do not recreate it. - **Never move files between `Configurations/` and `Deployment/`** without explicit instruction. `Configurations/` = live runtime confs (gitignored). `Deployment/` = templates and setup tooling (tracked). --- ## Project: What Varaverk Is Self-healing, self-maintaining, mutually-redundant two-server Unraid home media ecosystem. One codebase runs on both servers. No primary/standby — both run independently and cover each other. **HOST1 — unRAID-Gmer4Lfe** (`gmer4lfe@gmail.com`) - Hardware: Threadripper 1950X, 128 GB RAM, ZFS cache pools - Domain: Gmer4Lfe.com - Runs: full arr stack (Sonarr/Radarr/Lidarr), auth stack (source of truth), Emby primary **HOST2 — unRAID-Jayred365** - Hardware: Intel i5 10th gen, 64 GB RAM - Domain: Gmer4Lfe.us - Status: being rebuilt — most host2.conf sections scaffolded, not yet fully online Networking between hosts: Tailscale mesh. No hardcoded IPs — hostnames resolve via Tailscale. --- ## Configuration System (three-file model) Every script sources all three at startup: ``` master.conf ← shared: thresholds, toggles, profiles, orchestrator job lists host1.conf ← HOST1 credentials, shares, container names, keys host2.conf ← HOST2 credentials, shares, container names, keys ``` Sparse checkout (git) means each server only pulls its own `host*.conf`. HOST1 never sees HOST2 credentials and vice versa. **Rule:** thresholds/toggles → `master.conf`; credentials/paths/container names → `host*.conf`. `detect_hosts()` in `common.sh` matches `$(hostname)` against `HOST1`/`HOST2` in `master.conf` and sets `MY_ID` / `REMOTE_ID` for the rest of the script. --- ## Platform Adapter Layer `Plugin/unraid/adapter.sh` isolates all OS-specific calls. Scripts never branch on OS directly — always call adapter functions. This is intentional architecture — don't bypass it. --- ## Key Paths | Path | Purpose | |------|---------| | `master.conf` | Shared config — all thresholds, toggles, profiles | | `host1.conf` / `host2.conf` | Per-host credentials, shares, container lists | | `common.sh` | Shared functions — `detect_hosts()`, `log()`, `notify()`, etc. | | `load_config.sh` | Sources all three conf files + common.sh | | `State_Files/` | Runtime state (watchdogs, fallback, transcode) — survives reboots | | `data/` | Historical logs and stats | | `Plugin/unraid/` | Unraid WebGUI plugin (PHP pages, API endpoints, adapter) | | `Orchestrators/` | Top-level schedulers (array_started, daily, weekly, watchdog) | | `Watchdogs/` | docker_watchdog, system_watchdog, resource_watchdog, stability | | `Fallback/` | Mutual container failover logic | | `Rsync/` | rsync.sh + profile system | | `Media/` | Arr cleanup, discovery, permissions, play state sync | | `Tools/` | Manual one-off tools including `claude_startup.sh` | --- ## Orchestrator Schedule | When | What | |------|------| | Array start | `Orchestrators/array_started.sh` → runs `ARRAY_START_SCRIPTS` | | Every minute | `watchdog_orchestrator.sh` → resource → docker → system → stability watchdogs | | Every 30 min | `critical_sync_maintenance.sh` → downloaders_reset, play_state_sync, critical rsync | | Every 4 hours | `intermediate_sync_maintenance.sh` → arr_sync, arrs_failed_stalled_recovery | | Daily 1am | `daily_sync_maintenance.sh` → git pull, permissions, cleaners, arr cleanup, docker updates | | Sunday 2:30am | `weekly_sync_maintenance.sh` → full Emby + Critical-Data sync, weekly restarts | | Sunday 3am+ | `monthly_maintenance.sh` (self-gated on 30-day uptime) → ZFS scrub, SMART tests | | Sunday 7am | `sunday_morning_coffee_report.sh` → ZFS, SMART, certs, backup verify, bandwidth, Emby report | --- ## Rsync Toggle State (current) ```bash RSYNC_ENABLED=true CRITICAL_RSYNC_ENABLED=true INTERMEDIATE_RSYNC_ENABLED=true DAILY_RSYNC_ENABLED=true WEEKLY_RSYNC_ENABLED=true FALLBACK_RSYNC_ENABLED=true ``` --- ## Fallback System `fallback.sh` runs continuously from array start. States: `NORMAL | FALLBACK | NO_INTERNET | DARK` DDNS rules are absolute: - Internet loss → stop own DDNS immediately - Failover → start remote's DDNS as Tier 1 first - Handback → stop remote DDNS → rsync → start containers → start local DDNS last Tier delays before activating higher tiers are in `host*.conf` (`HOST1_TIER*_DELAY`, `HOST2_TIER*_DELAY`). --- ## Port Notes - **NPM admin API (`HOST1_NPM_URL`)** — port **7818**. Port 81 is the partnership WebUI port (`HOST1_PARTNERSHIP_AUTH_WEBUIS`), not the API. Easy to confuse. - **HOST1_NETWORK_WATCHDOG_NPM_URL** — external HTTPS domain, completely separate from the admin API. ## Known Gaps / Active Work - HOST2 NPM/lldap credentials (`HOST2_NPM_USER`, `HOST2_NPM_PASS`, `HOST2_LLDAP_PASS`) are empty in `host2.conf` — fill in when HOST2 is back online. - `PARTNERSHIP_ENABLED=false` — not yet active. - `FALLBACK_ENABLED=true` — fallback is running. --- ## Claude Code Persistence on Unraid `/root` is a RAM filesystem — wiped on every reboot. `Tools/claude_startup.sh` runs at array start (via `ARRAY_START_SCRIPTS`) and: - Symlinks `/root/.claude` → `/mnt/user/appdata/claude-code/.claude` - Symlinks `/root/.local/share/claude` → `/mnt/user/appdata/claude-code/local/share/claude` - Symlinks `/root/CLAUDE.md` → `/boot/config/plugins/varaverk/CLAUDE.md` (this file) This file lives on `/boot` (USB flash) and is always available regardless of array state. --- ## Commit Style Plain, concise messages. No Co-Authored-By trailers. No bullet-point summaries in the body. One sentence on the why, not the what.