Backup System
Automated daily backup of the Hermes Agent state to a dedicated private GitHub repo
(fducat18/hermes-backup). The engine is backup.sh, which uses a Grandfather-Father-Son
(GFS) rotation: daily snapshots, promoted to weekly on Sundays and monthly on the 1st,
with independent retention (7 daily / 4 weekly / 3 monthly).
What gets backed up
Section titled βWhat gets backed upβ| Category | Files | Notes |
|---|---|---|
| Identity | SOUL.md, CONTEXT.md, TODO.md |
Agent identity, glossary, task list |
| Memory | memories/MEMORY.md, memories/USER.md |
Agent memory + user profile |
| Config | config.yaml |
All settings |
| Skills | skills/ |
Custom skills |
| Cron | cron/jobs.json |
Scheduled job definitions |
| Scripts | scripts/ |
Custom automation (backup, security audit, etc.) |
| Plugins | plugins/ |
Installed plugins |
| Notes inbox | notes-inbox/ |
Ad-hoc notes until Drive + PARA integration |
| Non-default profiles | profiles/<name>/ |
Same selective structure: SOUL/CONTEXT/TODO, memories, config.yaml, profile.yaml, skills, scripts, plugins, cron/jobs.json |
What is NOT backed up (by design)
Section titled βWhat is NOT backed up (by design)β| Item | Reason |
|---|---|
.env, .env.bak.*, auth.json |
Secrets β stored in Bitwarden, never on GitHub |
state.db, sessions.db |
Session transcripts / runtime cache β too large for GitHub (>30 MB), recoverable from artifacts |
agent.log* |
Ephemeral runtime logs |
hermes-agent/ source |
Reinstallable via pipx; only custom patches are re-applied by scripts |
docs/ |
Separate repository |
cache/, logs/, node/, sandboxes/, pastes/, lsp/ |
Ephemeral or non-state |
Backup destination
Section titled βBackup destinationβRepo: fducat18/hermes-backup (private)
Each snapshot is a live directory (no tar.gz), placed in exactly one tier folder:
hermes-backup/βββ backup.sh # SSOT of the backup engineβββ restore.shβββ .gitignoreβββ backups/ βββ daily/ # MonβSat (7 retained) β βββ 20260821/ β βββ SOUL.md, CONTEXT.md, TODO.md β βββ config.yaml β βββ memories/ skills/ scripts/ plugins/ β βββ cron/jobs.json β βββ notes-inbox/ β βββ profiles/<name>/β¦ β βββ MANIFEST.txt βββ weekly/ # Sunday (4 retained) β βββ 20260816/ βββ monthly/ # 1st of month (3 retained) βββ β¦Exclusive tiers: each snapshot lives in exactly one tier folder β there is no
duplication across daily/, weekly/, monthly/. A snapshot on a Sunday lands only in
weekly/; one on the 1st only in monthly/; every other day only in daily/.
Date-only naming: snapshot folders use YYYYMMDD (no timestamp). This enables
same-day de-duplication β running the script twice in one day replaces that dayβs snapshot
instead of leaving duplicates β and keeps the rotation/retention logic simple.
Rotation & promotion
Section titled βRotation & promotionβRetention is enforced independently per tier:
| Tier | When created | Retention |
|---|---|---|
| Daily | Every day at 02:00 UTC | 7 snapshots |
| Weekly | Sunday (hard-link of the daily snapshot) | 4 snapshots |
| Monthly | 1st of month (hard-link of the latest weekly) | 3 snapshots |
Weekly and monthly snapshots are hard links (cp -al) to the underlying daily
snapshot β they share the same inodes, so they consume almost no extra disk. Rotating out
a daily snapshot that is also linked in weekly/monthly frees space only once all references
are gone.
Schedule & delivery
Section titled βSchedule & deliveryβ- Cron:
Hermes State Backupjob,0 2 * * *(02:00 UTC),no_agent: true - Script:
backup.sh(engine, stdout is the deliverable) - Repo:
~/workspace/hermes-backup/backup.shis the SSOT; a synced copy lives at~/.hermes/scripts/backup.shfor the cron job. Keep both in sync when editing. - Delivery: deterministic 4-line summary to Slack (status icon, date/time, per-tier rotation counts, git pack size). No file listing is sent.
Restore
Section titled βRestoreβFrom the hermes-backup repo:
./restore.sh backups/daily/20260821./restore.sh backups/weekly/20260816NOT restored (by design): secrets (.env, auth.json) β recreated manually from Bitwarden.
Post-upgrade recovery
Section titled βPost-upgrade recoveryβAfter hermes update (which overwrites the hermes-agent source checkout):
- Reinstall:
pipx install hermes-agent - Restore the dashboard security patch (Host-header validation):
python3 ~/.hermes/scripts/ensure_dashboard_security.py - Restart the dashboard:
systemctl --user restart hermes-dashboard
The dashboard security patch is also re-applied automatically by a watchdog cron job after upgrades.
Security
Section titled βSecurityβ- Secrets stay out of git:
.envandauth.jsonare excluded via the backup repoβs.gitignore, so nothing sensitive is ever committed. - Credentials are recreated from Bitwarden on restore β never stored in the backup repo.
- The backup repo is private; access control is GitHubβs.
- No bank data, no email, no sensitive personal data β just agent configuration and state.
- The backup script treats
~/.hermes/as read-only: it only copies from it, never modifies it.