diff --git a/.gitignore b/.gitignore index 23f6d63..56588f9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ Configurations/host*.conf Configurations/master.conf Configurations/*.bak -.claude .vscode # ── Runtime state, data, logs ───────────────────────────────────────────────── @@ -28,9 +27,6 @@ varaverk-*.txz # .txz packages are attached to GitHub releases, not committed to the repo. Plugin/dist/ -# ── Claude Code installation (lives alongside repo on flash, not source) ────── -claude-bin/ -claude-data/ # ── OS / editor ─────────────────────────────────────────────────────────────── .DS_Store diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 4d883ea..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,162 +0,0 @@ -# Varaverk — Claude Code Context - -## Working Rules (read first) - -- **Dev workspace** — `/mnt/cloud-storage/Important Shit/Git/Development/Varaverk/`. All editing happens here. -- **Prod** — `/boot/config/plugins/varaverk/`. Never edited directly. Only updated via `git pull` (daily orchestrator or the pull button in the UI). -- **No Co-Authored-By** in commit messages unless explicitly asked. -- **No comments** unless the WHY is genuinely non-obvious. - -### Current workflow: dev/prod split - -Edit in dev, push to Gitea, pull prod when ready (daily orchestrator or manual UI trigger). -Push is the only bridge — no deploy hooks, no rsync-on-save, no direct path references between dev and prod. - -`plugin_setup.sh` stays pointing at the prod path. Dev never touches `/boot/` directly. - -### 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. -- **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. diff --git a/Tools/claude_startup.sh b/Tools/claude_startup.sh deleted file mode 100755 index 9786bcd..0000000 --- a/Tools/claude_startup.sh +++ /dev/null @@ -1,181 +0,0 @@ -#!/bin/bash -# ============================================================================================== -# ================================= Claude Code Startup ======================================== -# ============================================================================================== -# -# PURPOSE -# ───────────────────────────────────────────────────────────────────────────── -# Restores Claude Code's persistent data after an Unraid reboot. -# /root is RAM — wiped on every boot. This script re-creates symlinks so -# Claude's memory, sessions, settings, and binary survive across reboots. -# -# Storage mode is read from the host conf file: -# -# HOST*_STORAGE_MODE_INTERNAL=true → Internal (boot) mode -# .claude data → /boot/config/claude -# binary → /boot/config/claude-bin -# No array dependency — runs even before array mounts. -# -# HOST*_STORAGE_MODE_INTERNAL=false → Appdata mode -# .claude data → /mnt/user/appdata/claude-code/.claude -# binary → /mnt/user/appdata/claude-code/local/share/claude -# Requires array to be mounted. -# -# On first run in either mode, migrates any existing live data to persistent -# storage. Subsequent runs only re-create the symlinks. -# -# Standalone script — no common.sh dependency. Safe to run directly from -# terminal or from array_started.sh. -# -# ============================================================================================== -# DESIGN PRINCIPLES -# ============================================================================================== -# -# Mode-Aware Paths -# Storage mode is read from host*.conf before any symlink is created. Internal -# mode (boot) requires no array — symlinks resolve immediately. Appdata mode -# requires the array to be mounted before symlinks are useful. -# -# Migrate on First Run -# If live data already exists at /root/.claude and the persistent store is -# empty, the live data is moved to persistent storage on first run. Subsequent -# runs only re-create the symlinks — migration is one-time. -# -# No common.sh Dependency -# Runs before load_config.sh is available (early in ARRAY_START_SCRIPTS). -# All logic is self-contained — no ecosystem functions used. -# -# ============================================================================================== -# OPERATIONAL SAFEGUARDS -# ============================================================================================== -# -# Conf probe — reads storage mode from Configurations/host*.conf directly -# Migration guard — only migrates if persistent store is empty; never overwrites -# Symlink-safe — removes existing symlink before re-creating; won't error on re-run -# -# ============================================================================================== -# RUNTIME MODES -# ============================================================================================== -# -# claude_startup.sh -# Set up persistent symlinks only — default, used by array_started.sh on boot. -# -# claude_startup.sh --launch -# Set up persistent symlinks and launch Claude interactively. -# -# ============================================================================================== - -LAUNCH=false -[[ "$1" == "--launch" ]] && LAUNCH=true - -_log() { echo " ✅ $*"; } -_warn() { echo " ⚠️ $*"; } -_err() { echo " ❌ $*" >&2; } - -echo "" -echo "━━━ Claude Code Startup ━━━" -echo "" - -# ── Detect storage mode ─────────────────────────────────────────────────────────────────────── -CONF_DIR="/boot/config/plugins/varaverk/Configurations" -STORAGE_INTERNAL=false -for _conf in "$CONF_DIR"/host*.conf; do - [[ -f "$_conf" ]] || continue - if grep -q "_STORAGE_MODE_INTERNAL=true" "$_conf" 2>/dev/null; then - STORAGE_INTERNAL=true - break - fi -done - -if [[ "$STORAGE_INTERNAL" == true ]]; then - CLAUDE_DATA="/boot/config/plugins/varaverk/claude-data" - CLAUDE_BIN="/boot/config/plugins/varaverk/claude-bin" - _log "Storage mode: internal boot" -else - PERSIST_DIR="/mnt/user/appdata/claude-code" - CLAUDE_DATA="$PERSIST_DIR/.claude" - CLAUDE_BIN="$PERSIST_DIR/local/share/claude" - _log "Storage mode: appdata" -fi - -# ── Array check (appdata mode only) ────────────────────────────────────────────────────────── -if [[ "$STORAGE_INTERNAL" == false ]]; then - if ! mountpoint -q /mnt/user 2>/dev/null; then - _err "Array not mounted — /mnt/user not available (required for appdata mode)" - exit 1 - fi -fi - -# ── Create persistent dirs ──────────────────────────────────────────────────────────────────── -mkdir -p "$CLAUDE_DATA" "$CLAUDE_BIN" - -# ── Migrate from old /boot/config/claude location (internal mode only) ─────────────────────── -if [[ "$STORAGE_INTERNAL" == true && -d /boot/config/claude && "$CLAUDE_DATA" != "/boot/config/claude" ]]; then - if [[ -z "$(ls -A "$CLAUDE_DATA" 2>/dev/null)" ]]; then - _warn "Migrating old /boot/config/claude → $CLAUDE_DATA" - cp -a /boot/config/claude/. "$CLAUDE_DATA/" - _log "Migrated .claude data from old location" - fi -fi - -# ── Migrate .claude on first run ────────────────────────────────────────────────────────────── -if [[ ! -L /root/.claude && -d /root/.claude ]]; then - _warn "First run — migrating /root/.claude → $CLAUDE_DATA" - cp -a /root/.claude/. "$CLAUDE_DATA/" - rm -rf /root/.claude - _log "Migrated .claude (memory, sessions, settings)" -elif [[ -z "$(ls -A "$CLAUDE_DATA" 2>/dev/null)" && -d /root/.claude ]]; then - _warn "Persistent storage empty — copying current .claude data" - cp -a /root/.claude/. "$CLAUDE_DATA/" - _log "Copied .claude data to persistent storage" -fi - -# ── Migrate Claude binaries on first run ────────────────────────────────────────────────────── -if [[ ! -L /root/.local/share/claude && -d /root/.local/share/claude ]]; then - _warn "First run — migrating Claude binaries → $CLAUDE_BIN" - cp -a /root/.local/share/claude/. "$CLAUDE_BIN/" - _log "Migrated Claude binaries" -fi - -# ── Create symlinks ─────────────────────────────────────────────────────────────────────────── -mkdir -p /root/.local/share /root/.local/bin - -[[ -d /root/.claude && ! -L /root/.claude ]] && rm -rf /root/.claude -ln -sfn "$CLAUDE_DATA" /root/.claude -_log ".claude → $CLAUDE_DATA" - -[[ -d /root/.local/share/claude && ! -L /root/.local/share/claude ]] && rm -rf /root/.local/share/claude -ln -sfn "$CLAUDE_BIN" /root/.local/share/claude -_log "claude binary → $CLAUDE_BIN" - -# ── Symlink CLAUDE.md ───────────────────────────────────────────────────────────────────────── -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 - _err "No Claude versions found in $CLAUDE_BIN/versions/" - _err "Install Claude Code first: npm install -g @anthropic-ai/claude-code" - exit 1 -fi - -ln -sfn "$CLAUDE_BIN/versions/$LATEST" /root/.local/bin/claude -_log "claude v$LATEST ready" - -echo "" - -if [[ "$LAUNCH" == false ]]; then - _log "Setup complete — run 'claude' to start" - echo "" - exit 0 -fi - -# ── Launch ──────────────────────────────────────────────────────────────────────────────────── -export PATH="$HOME/.local/bin:$PATH" -exec claude