# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is Backup + disaster-recovery tooling for a Linux server: Borg (offsite-synced via rclone) backing up `/home/srv/files/content`, including MariaDB running in Docker. The repo is unencrypted by deliberate operator choice on this deployment — `borg-backup.sh` prints a warning about it every run, which is expected, not a bug to fix. Plain bash, no build system, no CI, no test suite. `README.md` is the quickstart; `RUNBOOK.md` is the full operational doc (setup, deploy, cron, day-2 ops, recovery). - `borg-backup.sh` — daily backup orchestrator (cron). Never stops MariaDB: `dump_db.sh`'s `--single-transaction` dump is consistent on its own, and the raw data directory is excluded from the archive via a `.nobackup` marker file. - `dump_db.sh` — per-database `mysqldump`/`mariadb-dump` with atomic staging/swap. Deployed to `/opt/backup-agent/dump_db.sh` (borg-backup.sh invokes it at that exact path). It defaults to writing dumps next to itself, so borg-backup.sh always overrides `DUMP_DIR` to keep dumps inside `$TARGET` where borg can see them. - `restore.sh` — recovery CLI: `full` (disaster recovery), `db `, `file `, `--list-archives`, all supporting `--dry-run`. ## Global conventions (don't re-derive these) - `TARGET=/home/srv/files/content`, `REPO=/home/srv/files/backups/borg-2025`, `DB_CONTAINER=mariadb` - `BORG_PASSPHRASE_FILE=/root/.borg-passphrase` (chmod 600, read via `BORG_PASSCOMMAND`) - Backup DB user `backup` (password file `/root/.mariadb-backup.pw`, limited SELECT/LOCK grants) is separate from restore's `root` creds (`MYSQL_ROOT_PASSWORD` env or `/root/.mariadb-root.pw`) — restore needs CREATE/DROP privileges the backup user doesn't have. - Dumps live at `mariadb/dump/*.sql` plus `00-users-and-grants.sql`, inside `$TARGET`. - `restore.sh` and `borg-backup.sh` share one lockfile (`/var/lock/borg-backup.lock` via `flock -n 9`) so a restore and the nightly backup can never run concurrently. - Destructive-op safety: `full` refuses a non-empty `$TARGET` without `--force`; `db` requires typed dbname confirmation unless `--yes`; every mode supports `--dry-run`. - `resolve_archive()` in `restore.sh` deliberately does *not* filter archives by hostname (unlike backup's prune/list, which does) — disaster recovery may run from a different host than made the backup. - Logs: `/var/log/borg/{backup,restore}-*.log` (override with `BORG_BACKUP_LOGFILE` / `RESTORE_LOGDIR` for local testing). ## Verifying changes No test suite. Verify script changes with `bash -n