--- name: hermes-state-backup description: Push a full Hermes state snapshot to a git backup repo. For merging between instances, use hermes-state-merge instead. version: 1.0.0 metadata: hermes: tags: [backup, state, git, snapshot, skills, memory, config, cron] --- # Hermes State Backup Push a full snapshot of the current Hermes instance state to a git remote backup repository. This is a one-way push — the backup repo is the destination, not the source. For pulling state from backup (merging between instances), use `hermes-state-merge`. ## When to Use - Periodic backup of the current instance's skills, memory, config, and cron - Before major changes (Hermes upgrade, config overhaul, skill reorganization) - When the user explicitly asks to back up ## Trigger User says "back up Hermes", "backup now", "push to backup repo", or similar. ## What Gets Backed Up | Path | Included? | Notes | |------|-----------|-------| | `skills/` | YES | rsync --delete to mirror current | | `memories/` | YES | MEMORY.md + USER.md | | `memory_store.db` | YES | ~250K, small enough for git | | `config.yaml` | YES | Copy directly | | `SOUL.md` | YES | Copy directly | | `cron/` | YES | rsync --delete, includes jobs.json + output/ | | `.env` | NO | Secrets — never commit to git | | `state.db` | NO | 600MB+, git cannot delta-compress, already in .gitignore | | `auth.json` | NO | OAuth tokens, instance-specific | | `sessions/` | NO | Too large, regenerable | | `checkpoints/`, `logs/`, `*_cache/` | NO | Runtime artifacts | ## Workflow The backup repo lives as a permanent working copy at `~/.hermes/backup-repo`. Do NOT clone to a temp directory — use the existing clone. ### Step 1: Clean and Pull ```bash cd ~/.hermes/backup-repo git fetch origin git reset --hard origin/main git clean -fdx -e .git ``` The `git reset --hard` + `git clean -fdx -e .git` is critical — any uncommitted changes from a prior failed backup or concurrent cron output will abort the checkout. ### Step 2: Mirror Current State ```bash cd ~/.hermes/backup-repo # Skills, memories, cron — full mirror with cleanup rsync -av --delete ~/.hermes/skills/ skills/ rsync -av --delete ~/.hermes/memories/ memories/ rsync -av --delete ~/.hermes/cron/ cron/ # Single files — direct copy cp ~/.hermes/config.yaml config.yaml cp ~/.hermes/SOUL.md SOUL.md cp ~/.hermes/memory_store.db memory_store.db ``` ### Step 3: Update Manifest ```bash NOW=$(date '+%Y-%m-%d %H:%M:%S %z') HOST=$(hostname) printf 'Hermes state backup\nGenerated: %s\nHost: %s\nHermes home: %s\nRemote: \nIncluded:\n- skills/\n- memories/\n- memory_store.db\n- config.yaml\n- SOUL.md\n- cron/\n' "$NOW" "$HOST" "$HOME/.hermes" > BACKUP_MANIFEST.txt printf 'Source file mtimes:\n' > BACKUP_SOURCES.txt stat -c '%y %n' ~/.hermes/skills ~/.hermes/memories ~/.hermes/memory_store.db \ ~/.hermes/config.yaml ~/.hermes/SOUL.md ~/.hermes/cron >> BACKUP_SOURCES.txt ``` ### Step 4: Commit and Push ```bash git add -A git commit -m "Auto backup: $(date '+%Y-%m-%d %H:%M:%S %z')" git push origin main ``` ## Pitfalls 1. **Never include `state.db`** — it's 600MB+ per snapshot, git cannot delta-compress. Already in `.gitignore` but verify after `rsync --delete` that it wasn't accidentally picked up. 2. **Must clean dirty tree first** — if the backup-repo has uncommitted changes (e.g. from a manual edit or prior failed backup), `git checkout` or subsequent pulls will fail. Always `git reset --hard` + `git clean` first. 3. **`.env` is blocked by user** — do not attempt to redact or copy `.env` into the backup repo. The user has explicitly blocked this. `.env` content is already covered by memory/skills, and secrets should not be in git. 4. **Large number of deleted cron outputs is normal** — after 2+ months without backup, `git status` may show 10,000+ deleted cron output files. This is expected cleanup, not data loss. 5. **`config.yaml` may contain API keys** — the copy is a raw snapshot. The user is responsible for redacting before push if needed. Do not apply sed transforms without explicit approval. ## Cron Backup: Gateway Must Be Running Cron jobs (including backup) **will not fire** unless the Hermes gateway background service is running. The gateway handles the scheduler ticker. CLI-only Hermes sessions do NOT automatically run cron. ### Diagnosis ```bash hermes cron status # ✗ Gateway is not running — cron jobs will NOT fire # ✓ Gateway is running — cron jobs will fire automatically ``` - `next_run_at` stuck in the past + `last_run_at` = null → scheduler never fired - `ticker_heartbeat` / `ticker_last_success` timestamps in `~/.hermes/cron/` show last tick (stale = scheduler dead) ### Fix ```bash hermes gateway install # one-time: install as systemd user service systemctl --user status hermes-gateway # verify running hermes cron status # confirm: ✓ Gateway is running ``` After gateway starts, all due cron jobs fire immediately, then follow their schedule. ### Two Backup Scripts Conflict If both `backup.sh` and `hermes-backup.sh` target the same backup-repo, they race on `.git/index.lock` when they fire simultaneously at gateway startup. Remove one job or use separate repos. ## Related Skills - **hermes-state-merge** — Pull state FROM backup repo to local (merge, not overwrite). Use after another instance pushes a backup. - **hermes-agent** — General Hermes configuration, migration, and troubleshooting. See the "Migration & Backup" section for full-instance migration.