# DR Issue Log > Permanent record of all disaster recovery audit findings, root causes, fixes, and verification dates. > Updated: 2026-07-09 --- ## Summary **Fixed** [OK] - **DR-001** [HIGH] Caddyfile not backed up -> added to backup scripts - **DR-002** [MED] Systemd backup path wrong -> corrected to /etc/systemd/system/ - **DR-003** [MED] migration-creds.txt not backed up -> added to backup scope - **DR-004** [MED] dre-temp-passwords.txt 644->600 - **DR-005** [MED] migration-creds.txt 644->600 - **DR-006** [HIGH] Full backup stale (last Jul 5) -> manual backup ran Jul 10 (527MB), system crontab added at 1 AM daily - **DR-007** [HIGH] home-router backup cron error -> switched to run-wisp-backup.sh - **DR-008** [HIGH] MikroTik backup stale -> installed deps, fixed IP - **DR-010** [MED] Failover timing -> 5min/2min from 10min/3.5min - **DR-011** [HIGH] S3 buckets system-configs & docker-volumes created, versioned, IAM updated - **DR-012** [MED] firecrawl-usage-check crash on KeyError 'monthly' -> hardened load() - **DR-013** [LOW] ops collector could not find Hetzner token -> added .hetzner_token fallback **Resolved** [INFO] - **DR-009** app1-bu DR plan mismatch -> server stays warm per design - **DR-014** [INFO] home-router-daily-backup paramiko error -> verified working via live test Jul 10 **Investigating / Blocked** [PENDING] - **DR-015** [MED] service-health-check / apex-mail-watchdog failing on real remote outages (wphost02, WireGuard) - **DR-016** [HIGH] root-essentials-backup silently failing — tarballs created locally but not uploaded to S3 since Jul 12 - **DR-017** [HIGH] WISP CCR tower configs not backed up — only one old config (Jul 7) in mikrotik-ccr-backups - **DR-018** [MED] wphost02 backup singular — only one backup from Jul 10, no recurring schedule - **DR-019** [MED] SiteGround WordPress backup not implemented — siteground/ prefix empty - **DR-011b** [HIGH] system-configs & docker-volumes sync buckets still empty — 0 objects, scripts exist but not populating --- ## 2026-07-08 -- Initial Full DR Audit ### DR-001 -- Caddyfile not backed up `[HIGH] [OK] Fixed` **Problem** `/etc/caddy/Caddyfile` was not copied by `hermes-backup.sh` or `hermes-live-sync.sh`. If Core server fails, the reverse proxy config would need to be rebuilt from scratch. **Root Cause** Backup scripts were written to cover Hermes config and user directories but omitted system-level config files entirely. **Fix** Added `/etc/caddy/Caddyfile` to both `hermes-backup.sh` and `hermes-live-sync.sh` with DR FIX comments dated 2026-07-08. **Verification** - [OK] `bash -n` syntax check passed on both scripts - [OK] Subagent confirmed all paths referenced correctly --- ### DR-002 -- Systemd service backup path wrong `[MED] [OK] Fixed` **Problem** `hermes-backup.sh` collected systemd services from `~/.config/systemd/user/` instead of `/etc/systemd/system/`. Real service files (`hermes-agent.service`, `shark-game.service`, etc.) were not backed up. **Root Cause** Backup script path pointed to user-level systemd directory instead of system-level. **Fix** Corrected path to `/etc/systemd/system/*.service` in `hermes-backup.sh`. Embedded restore script also updated. **Verification** - [OK] Post-fix script syntax check - [OK] Subagent confirmed correct paths --- ### DR-003 -- migration-creds.txt not in backup scope `[MED] [OK] Fixed` **Problem** `/root/.hermes/migration-creds.txt` existed but wasn't referenced by any backup script. **Root Cause** File was added to the system after backup script was written, never included in scope. **Fix** Added to `hermes-backup.sh` file list with `chmod 600` restore instruction. **Verification** - [OK] Post-fix syntax check - [OK] File confirmed present and included --- ### DR-004 -- dre-temp-passwords.txt exposed `[MED] [OK] Fixed` **Problem** `/root/.hermes/references/dre-temp-passwords.txt` was readable by all users (644) instead of owner-only (600). **Root Cause** Script created the file without explicit permission setting. **Fix** `chmod 600` **Verification** - [OK] `ls -la` confirms `-rw-------` --- ### DR-005 -- migration-creds.txt exposed `[MED] [OK] Fixed` **Problem** Same as DR-004 -- `migration-creds.txt` was 644. **Root Cause** Written without explicit permission setting. **Fix** `chmod 600` **Verification** - [OK] `ls -la` confirms `-rw-------` --- ### DR-006 -- Full backup stale `[HIGH] [OK] Fixed 2026-07-10` **Problem** `hermes-full-backup.tar.gz` last uploaded to S3 on Jul 5. Daily 5 AM cron had missed 3 days. **Root Cause** No cron job scheduled the backup script. `hermes-backup.sh` existed and was correct but was never wired to cron via Hermes or system crontab. The gateway lifecycle guard (#30719) blocked running it as a Hermes cron job. **Fix** - Manually ran `hermes-backup.sh` on Jul 10 — produced 527MB tarball, uploaded successfully - Added system crontab entry: `0 1 * * * /root/.hermes/scripts/hermes-backup.sh 2>&1 | logger -t hermes-full-backup` - Added audit watchdog: `0 2 * * * /root/.hermes/scripts/backup-audit-check.sh 2>&1 | logger -t backup-audit` **Verification** - [OK] Manual backup completed: `hermes-full-backup-2026-07-10.tar.gz` (527,318,371 bytes) in S3 - [OK] Crontab entry confirmed active: `crontab -l` - [OK] Audit script in place to verify backup completion at +1h --- ### DR-007 -- home-router backup cron error `[HIGH] [OK] Fixed` **Problem** Cron job errored at 06:00 -- backup script failed. SSH export on router produced a stuck `.in_progress` file that blocked SCP. **Root Cause** Cron was using `home-router-backup.sh` (old WireGuard tunnel script) instead of the proper `run-wisp-backup.sh` pipeline. Stuck export files blocked SCP -> S3 upload failed. **Fix** - Changed cron to use `run-wisp-backup.sh` - Cleaned stuck `.in_progress` files from router - Installed missing packages: `paramiko v5.0.0`, `xl2tpd`, `strongSwan` **Verification** - [OK] Backup ran end-to-end - [OK] Config uploaded to `s3://mikrotik-ccr-backups/wisp-backups/configs/home/2026-07-08/` --- ### DR-008 -- MikroTik CCR backup stale `[HIGH] [OK] Fixed` **Problem** `mikrotik-ccr-backups` S3 bucket had no uploads since Jul 5. Two compounding root causes prevented backups. **Root Causes** 1. `wisp-backup.py` failed because `paramiko` was not installed 2. Tower IP in `config.yaml` was `192.168.88.1` (LAN) but SSH is restricted to WireGuard tunnel network `10.77.0.0/24` **Fix** - Installed `paramiko v5.0.0` - Updated tower IP to `10.77.0.2` in `wisp-backup/config.yaml` - Installed missing VPN stack: `xl2tpd`, `strongSwan` **Verification** - [OK] Backup ran end-to-end - [OK] Config uploaded to S3 successfully --- ## 2026-07-08 -- Failover Logic Update ### DR-009 -- app1-bu DR plan mismatch `[INFO] [OK] Resolved` **Problem** DR plan doc stated "offline, boots on demand" but server was running (3 days uptime). **Root Cause** DR plan documentation was outdated. Actual design is **warm standby** -- always on with Hermes dormant. **Fix** Updated DR plan to reflect warm standby design. Server stays running. **Verification** - [OK] Docs corrected - [OK] Server continues as-is --- ### DR-010 -- Failover timing adjustment `[MED] [OK] Fixed` **Problem** Failover detection was too slow: 10-minute check intervals with 3.5-minute confirmation window. **Change** - **Check interval:** Every 10 min -> **Every 5 min** - **Confirmation:** 4 x 60s (3.5 min) -> **4 x 30s (2 min)** - **Max downtime:** ~13.5 min -> **~7 min** **Rationale** Faster detection = shorter failover window. If Core doesn't respond within 2 min of constant checking, app1-bu activates. **Verification** - [OK] Cron changed to `*/5 * * * *` - [OK] Watchdog updated: 30s × 4 cycles = 2 min confirmation - [OK] Verified via SSH on app1-bu --- ## 2026-07-09 -- Session Findings (Ops/Infra pass) ### DR-006 -- Full backup stale (UPDATE: root cause found) `[HIGH] [PENDING] Blocked` **Problem** `hermes-full-backup` in Wasabi (`s3://hermes-vps-backups/hermes-full-backup/`) last uploaded Jul 5. Ops collector reports it `critical` (age > 72h). **Root Cause (identified 2026-07-09)** `hermes-backup.sh` exists and is correct (uploads the full tarball to the right path), but there is NO Hermes cron job that runs it. The 21 jobs in `cron/jobs.json` include `hermes-live-sync` (every 15m -> `live/` path, healthy) but nothing that runs the daily full backup. The DR-006 note about a `run-hermes-backup.sh` wrapper was never wired to cron. **Fix (pending -- requires cron creation, terminal approval-gated this session)** Create a daily cron job that runs the full backup, e.g.: ``` hermes cron create --name hermes-full-backup --schedule "0 5 * * *" \ --script hermes-backup.sh --no-agent --deliver local ``` Or via cronjob(action='create', name='hermes-full-backup', schedule='0 5 * * *', script='hermes-backup.sh', no_agent=True, deliver='local'). After first run, verify the collector flips this bucket to `ok`. **Status** - [PENDING] `hermes-backup.sh` verified correct by inspection - [PENDING] Cron job must be created to schedule it --- ### DR-011 -- S3 buckets missing + sync not scheduled `[HIGH] [OK] Fixed 2026-07-10` **Problem** Ops collector reported `itpropartner-system-configs` and `itpropartner-docker-volumes` as `NoSuchBucket`. These are the normalized buckets from the Jul 8 S3 plan. **Root Cause** Two compounding issues: 1. The buckets were never created. The Hermes-User IAM key lacked `s3:CreateBucket`, so they had to be created in the Wasabi Console web UI (manual step, never done). 2. The sync scripts (`hermes-system-config-sync.sh`, `hermes-docker-sync.sh`) exist and are correct, but had no cron job scheduling them. **Fix (completed Jul 10)** 1. Created both buckets via Wasabi API: `itpropartner-system-configs`, `itpropartner-docker-volumes` 2. Enabled versioning on both buckets via `aws s3api put-bucket-versioning` 3. Updated Hermes-User IAM policy to include both bucket ARNs 4. Verified PUT/GET/DELETE operations work on both buckets 5. Sync scripts confirmed present and executable — cron scheduling pending (tracked as DR-011b) **Verification** - [OK] Both buckets exist and respond to S3 operations - [OK] Versioning enabled on both - [OK] IAM policy updated with bucket ARNs - [OK] PUT/GET/DELETE tested successfully - [PENDING] Sync cron jobs to populate buckets (DR-011b) --- ### DR-012 -- firecrawl-usage-check crash `[MED] [OK] Fixed` **Problem** `firecrawl-usage-check` cron errored: `KeyError: 'monthly'` in `track-firecrawl.py summary()`. **Root Cause** `load()` returned the raw JSON from `firecrawl-usage.json`. An older state file lacked the `monthly` key, so `data["monthly"].get(...)` raised KeyError. The loader was not schema-safe. **Fix** Rewrote `load()` in `/root/.hermes/scripts/track-firecrawl.py` to start from a defaults dict and merge the file on top, then coerce `monthly`/`calls`/`total_used` to correct types. This is forward/backward compatible with partial or corrupt state files. **Verification** - [OK] `patch` lint (py_compile) passed - [OK] Current `firecrawl-usage.json` already contains `monthly` key -> next run will pass --- ### DR-013 -- Ops collector could not read Hetzner token `[LOW] [OK] Fixed` **Problem** `ops-status.json` showed `hetzner_servers: [{status: error, message: "HETZNER_API_TOKEN not found"}]`, so the server inventory on the ops portal was empty. **Root Cause** `collect_hetzner_servers()` only looked in `os.environ` and `.env`. The Hetzner token on this box lives in the file `/root/.hermes/scripts/.hetzner_token` (used by `snapshot-hetzner.py`), which the collector never checked. **Fix** Added a fallback in `collect_hetzner_servers()` to read `/root/.hermes/scripts/.hetzner_token` when env/.env lookups miss. (`ops-data-collector.py`) **Verification** - [OK] `patch` lint passed - [PENDING] Confirm inventory populates on next collector run (needs .hetzner_token present) --- ### DR-014 -- home-router-daily-backup paramiko error `[INFO] [OK] Resolved 2026-07-10` **Problem** Job showed `last_status: error` with `ModuleNotFoundError: No module named 'paramiko'`. **Root Cause** The error was from the 06:00 Jul 9 run. paramiko had previously been installed against Python 3.11 (leftover `wisp-backup.cpython-311.pyc`), but the system `python3` is now 3.13. **Resolution** paramiko IS present for the current interpreter: `/usr/local/lib/python3.13/dist-packages/paramiko/`. The error was a stale artifact from before the py3.13 install was confirmed. **Live Test Verification (Jul 10)** - [OK] Ran `wisp-backup.py` manually — completed successfully - [OK] WireGuard tunnel to 10.77.0.2 confirmed up - [OK] Config fetched and uploaded to `s3://mikrotik-ccr-backups/wisp-backups/configs/home/2026-07-10/` (3,898 bytes) - [OK] Logs fetched and uploaded - [OK] Exit: 1 OK, 0 Failed - [OK] No paramiko import error — resolved for python3.13 --- ### DR-015 -- health-check / apex watchdog failing on remote outages `[MED] [PENDING] External` **Problem** `service-health-check` and `apex-mail-watchdog` report errors every cycle. **Root Cause** Genuine remote conditions, not script bugs: - `wphost02` (5.161.62.38) SSH is refused -> apex watchdog SMTP test cannot run. - WireGuard tunnel to home router (10.77.0.2) is down -> health check + Home-Router-Watchdog fail. - Portal mockup on port 8081 and MySQL tunnel targets are also unreachable. Note: the current `apex-mail-watchdog.sh` does LOGIN-only SMTP tests (no test email sent), so it is NOT the source of the "apex test emails" complaint -- that was an earlier version. **Fix** No code change. These clear when wphost02 SSH and the WireGuard tunnel are restored (part of the broader migration / home-router work). Documented so the errors are understood, not chased as script bugs. **Status** - [PENDING] External dependency -- resolves when remote hosts/tunnel are back online --- ## 2026-07-15 -- Full DR Audit ### DR-016 -- root-essentials-backup silently failing `[HIGH] [PENDING]` **Problem** root-essentials-backup ran at 3 AM daily per system crontab but tarballs were NOT being uploaded to S3. S3 only has Jul 10 and Jul 11. Local tarballs accumulated in /tmp (Jul 12-15, 4 files) proving the script runs and creates archives but the upload step fails silently. **Root Cause** The script runs past tar creation (local tarballs exist) but the aws s3 cp upload step fails. With set -euo pipefail the script exits on the upload failure, leaving the tarball in /tmp (cleanup never runs). Exact cause of upload failure TBD -- possible S3 permission change or endpoint issue. Also: the Caddyfile path on line 30 is wrong (`-C /etc systemd/system/caddy/Caddyfile` should be `-C /etc caddy/Caddyfile`), but this is masked by `|| true`. **Fix** Pending investigation. Need to run the script manually with verbose output to identify the upload failure. **Status** - [PENDING] Investigating --- ### DR-017 -- WISP CCR tower configs not backed up `[HIGH] [PENDING]` **Problem** mikrotik-ccr-backups bucket contains home-gateway configs (daily Jul 9-15) but only one WISP CCR config from Jul 7 (`2026-07-07-config.rsc`). No tower router configs since then. **Root Cause** wisp-backup.py script references VPN and CCR towers in its docstring, but only the home gateway configs appear in S3. The script may not be reaching the tower CCRs, or the CCR backup path may be misconfigured in config.yaml. **Fix** Pending investigation. Need to review wisp-backup/config.yaml and test connectivity to WISP CCR towers. **Status** - [PENDING] Investigating --- ### DR-018 -- wphost02 backup not recurring `[MED] [PENDING]` **Problem** Only one wphost02 backup exists on S3: wphost02-backup-2026-07-10.tar.gz (656 MB). No recurring backup schedule. MariaDB dumps on wphost02 and fleettracker360 were flagged as known gaps in the Jul 10 DR audit but not yet resolved. **Root Cause** Backup was a one-time manual capture during the Jul 10 DR audit. No cron job was created for recurring wphost02 backups. **Fix** Pending. Needs a cron job on wphost02 to run regular backups to S3. **Status** - [PENDING] Known gap from Jul 10 audit, not yet resolved --- ### DR-019 -- SiteGround WordPress backup not implemented `[MED] [PENDING]` **Problem** siteground/ prefix in hermes-vps-backups is completely empty. No SiteGround WordPress site backups exist on S3. MainWP + WPvivid Pro backs up 15 sites to Wasabi independently, but sites not in MainWP have no S3 backup. **Root Cause** Fleet-wide SFTP backup timed out at 600 seconds on Jul 10. No alternative was deployed for sites outside MainWP coverage. **Fix** Pending. Either batch SFTP backups in groups of 3-5 or extend MainWP coverage to remaining sites. **Status** - [PENDING] Known gap, not yet resolved --- ### DR-011b -- system-configs & docker-volumes sync still empty `[HIGH] [PENDING]` **Problem** Both itpropartner-system-configs and itpropartner-docker-volumes buckets exist on Wasabi with versioning enabled, but contain 0 objects. The sync scripts (hermes-system-config-sync.sh, hermes-docker-sync.sh) exist and have correct syntax + venv activation, but no Hermes cron jobs are creating them. **Root Cause** Cron jobs were never created for these sync scripts. The hermes-live-sync was split into purpose-specific scripts on Jul 8 but the corresponding cron jobs were deferred. **Fix** Create two no_agent cron jobs: hermes-system-config-sync (every 15m) and hermes-docker-sync (every 15m). **Status** - [PENDING] Deferred since Jul 10