CLAUDE.md previously said the repo was encrypted, which was true when written but no longer matches this deployment - the operator chose to stay unencrypted, so the recurring "not encrypted" warning is expected behavior, not something to fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.4 KiB
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-transactiondump is consistent on its own, and the raw data directory is excluded from the archive via a.nobackupmarker file.dump_db.sh— per-databasemysqldump/mariadb-dumpwith 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 overridesDUMP_DIRto keep dumps inside$TARGETwhere borg can see them.restore.sh— recovery CLI:full(disaster recovery),db <name>,file <path>,--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=mariadbBORG_PASSPHRASE_FILE=/root/.borg-passphrase(chmod 600, read viaBORG_PASSCOMMAND)- Backup DB user
backup(password file/root/.mariadb-backup.pw, limited SELECT/LOCK grants) is separate from restore'srootcreds (MYSQL_ROOT_PASSWORDenv or/root/.mariadb-root.pw) — restore needs CREATE/DROP privileges the backup user doesn't have. - Dumps live at
mariadb/dump/*.sqlplus00-users-and-grants.sql, inside$TARGET. restore.shandborg-backup.shshare one lockfile (/var/lock/borg-backup.lockviaflock -n 9) so a restore and the nightly backup can never run concurrently.- Destructive-op safety:
fullrefuses a non-empty$TARGETwithout--force;dbrequires typed dbname confirmation unless--yes; every mode supports--dry-run. resolve_archive()inrestore.shdeliberately 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 withBORG_BACKUP_LOGFILE/RESTORE_LOGDIRfor local testing).
Verifying changes
No test suite. Verify script changes with bash -n <script>.sh (syntax) and shellcheck <script>.sh, and by tracing the change against RUNBOOK.md's documented behavior. Both borg-backup.sh and restore.sh prepend a hardened PATH (/usr/local/sbin:/usr/local/bin:...:$PATH) and require flock, borg, docker, and a mysql/mariadb client to actually exercise end-to-end — this only really runs on the target Linux server, not locally on macOS (flock(1) doesn't even exist there).
Conventions
- Conventional commits (
feat:,fix:,docs:,chore:). Direct commits tomaster, no PR workflow. - New restore/backup logic should mirror the existing style in
borg-backup.sh/restore.sh:log()/step()/die()/run_cmd()helpers,set -euo pipefail, config constants up top. - Run
shellcheckon any script you touch before considering it done.