Files
kbeandClaude Sonnet 5 dc414de14e docs: add README, note repo is unencrypted by deliberate choice
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>
2026-07-25 21:35:54 +02:00

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-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 <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=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 <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 to master, 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 shellcheck on any script you touch before considering it done.