#!/usr/bin/env bash # Push the irreplaceable-and-small state to a private off-site git repo. # # Closes the last gap from docs/incidents.md #5: local snapshots survive # `git clean -fdx` because they live outside the repo, but they do NOT survive # the disk. This does. # # WHAT IS SYNCED # .omo/ plan artifacts + evidence ledger (13MB, text, versions well) # systemd units tuned: graceful-shutdown fix, timers # opencode plugins session-registry.js etc. -- hand-written, not from a package # claude memory cross-session project memory # config.local.yaml operator tariff/overrides (personal, NOT a credential) # router.db gzipped, at most once per day -- see SIZE below # # WHAT IS DELIBERATELY NOT SYNCED # .env — the provider API key. There is no usable secret key on this host to # encrypt it to (only public keys), so it would land in git history in # plaintext, and git history is forever even in a private repo. An API # key is REPLACEABLE: regenerate it from the provider. 22,000 energy # observations are not. It stays in the local backups only. # .venv, node_modules — rebuildable from pinned requirements. # ollama models — ~30GB, re-pullable; recipes in docs/local-models.md. # # SIZE: router.db is ~5MB gzipped and binary, so git cannot delta it. It is # committed at most DAILY (not hourly) to keep growth near 5MB/day rather than # 120MB/day. Set SYNC_DB=0 to skip it entirely and rely on local snapshots. set -euo pipefail REPO="${REPO:-$HOME/Sources/6krrt}" REMOTE="${OFFSITE_REMOTE:-ssh://git@git.adlee.work:2222/alee/6kbackups.git}" WORK="${OFFSITE_WORKDIR:-$HOME/.local/share/6krrt-offsite}" SYNC_DB="${SYNC_DB:-1}" if [ ! -d "$WORK/.git" ]; then git clone "$REMOTE" "$WORK" 2>/dev/null || { mkdir -p "$WORK"; git -C "$WORK" init -q; git -C "$WORK" remote add origin "$REMOTE"; } fi cd "$WORK" git fetch -q origin 2>/dev/null || true git checkout -q -B main 2>/dev/null || true git reset -q --hard origin/main 2>/dev/null || true rsync -a --delete "$REPO/.omo/" ./omo/ 2>/dev/null || true mkdir -p home config rsync -a --delete "$HOME/.config/systemd/user/" ./home/systemd-user/ 2>/dev/null || true rsync -a --delete "$HOME/.config/opencode/" ./home/opencode/ --exclude 'cache/' --exclude 'log/' --exclude '*.log' 2>/dev/null || true rsync -a --delete "$HOME/.claude/projects/-home-alee-Sources-6krrt/memory/" ./home/claude-memory/ 2>/dev/null || true [ -f "$REPO/config/config.local.yaml" ] && cp -f "$REPO/config/config.local.yaml" ./config/ # Daily DB snapshot, same filename so the working tree stays flat. if [ "$SYNC_DB" = "1" ]; then today=$(date +%Y-%m-%d) if [ ! -f .db-stamp ] || [ "$(cat .db-stamp)" != "$today" ]; then tmp=$(mktemp) sqlite3 "$REPO/router.db" ".backup '$tmp'" [ "$(sqlite3 "$tmp" 'SELECT integrity_check FROM pragma_integrity_check LIMIT 1;')" = "ok" ] \ && { gzip -c "$tmp" > router.db.gz; echo "$today" > .db-stamp; } \ || echo "db snapshot failed integrity_check; not synced" >&2 rm -f "$tmp" fi fi cat > README.md <<'INNER' # 6krrt workstation backup Off-site copy of state the main repo does not carry. See `docs/incidents.md` #5 in the main repo for why this exists. **Not here on purpose:** `.env` (provider API key — regenerate it instead; there is no usable secret key on the source host to encrypt it to), `.venv`, `node_modules`, and Ollama models. Restore: clone the main repo, rebuild the venv from pinned requirements, then copy `omo/` back to `.omo/`, `config/config.local.yaml` into place, and `home/*` to their `~/.config` locations. `router.db.gz` is a daily snapshot. INNER git add -A if git diff --cached --quiet; then echo "offsite: no changes" else git -c user.name="6krrt-backup" -c user.email="backup@localhost" \ commit -q -m "backup $(date -Iseconds)" git push -q -u origin main && echo "offsite: pushed $(git rev-parse --short HEAD)" fi