From 857d47b4f308e85f395a0afbbcee0f808ed28d7c Mon Sep 17 00:00:00 2001 From: Otto Date: Sat, 20 Jun 2026 11:16:49 +0200 Subject: [PATCH] backup: limit local second-brain retention --- cron_tasks/backup_secondbrain.py | 43 ++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/cron_tasks/backup_secondbrain.py b/cron_tasks/backup_secondbrain.py index 67758c2..419226c 100644 --- a/cron_tasks/backup_secondbrain.py +++ b/cron_tasks/backup_secondbrain.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 -"""Backup-Task für Second Brain - isoliert, persistent.""" +"""Backup-Task fuer Second Brain - isoliert, persistent. + +Local retention is intentionally small on the OpenClaw host: keep only the +newest two JSONL exports. Longer-term/off-host retention should be handled by +an encrypted artifact/package workflow, not by committing raw brain exports to +Git. +""" import json, os, sys from pathlib import Path from datetime import datetime, timezone @@ -8,14 +14,47 @@ BRAIN_DIR = Path("/root/.openclaw/workspace/second-brain") sys.path.insert(0, str(BRAIN_DIR)) from src.store import EngramStore +DEFAULT_KEEP_LOCAL_BACKUPS = 2 + +def backup_sort_key(path: Path) -> tuple[float, str]: + return (path.stat().st_mtime, path.name) + +def prune_local_backups(data_dir: Path, keep: int = DEFAULT_KEEP_LOCAL_BACKUPS) -> list[str]: + """Keep only the newest local backup files. + + Both uncompressed `.jsonl` and compressed `.jsonl.gz` exports are counted, + so historical compressed backups do not silently accumulate again. + """ + keep = max(1, keep) + backups = sorted( + list(data_dir.glob("backup_*.jsonl")) + list(data_dir.glob("backup_*.jsonl.gz")), + key=backup_sort_key, + reverse=True, + ) + removed: list[str] = [] + for backup in backups[keep:]: + backup.unlink() + removed.append(str(backup)) + return removed + def main(): brain_db = os.environ.get("BRAIN_DB", str(BRAIN_DIR / "data" / "brain.sqlite")) + keep_local = int(os.environ.get("SECOND_BRAIN_BACKUP_KEEP_LOCAL", str(DEFAULT_KEEP_LOCAL_BACKUPS))) store = EngramStore(brain_db) ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S") backup_path = Path(brain_db).parent / f"backup_{ts}.jsonl" count = store.export_jsonl(str(backup_path)) - result = {"timestamp": datetime.now(timezone.utc).isoformat(), "backup_path": str(backup_path), "count": count, "success": True} + removed = prune_local_backups(Path(brain_db).parent, keep_local) + result = { + "timestamp": datetime.now(timezone.utc).isoformat(), + "backup_path": str(backup_path), + "count": count, + "removed_old_backups": removed, + "keep_local": keep_local, + "success": True, + } print(f"BACKUP: {count} Engramme -> {backup_path}") + print(json.dumps(result, ensure_ascii=True)) return 0 if __name__ == "__main__":