---
name: server-recovery-bundle
description: "Build self-contained recovery bundles containing all config, scripts, credentials, cron jobs, and conversation history needed to restore a Hermes instance onto a brand-new server."
version: 1.1.0
author: Hermes Agent
license: MIT
platforms: [linux]
metadata:
  hermes:
    tags: [disaster-recovery, backup, migration, bootstrap, hermetic]
    related_skills: [email-workflows, hermes-agent, hermes-backup]
---

# Server Recovery Bundle

Build a single self-contained markdown file that can be pasted into a fresh Hermes agent to fully restore the instance. Think of it as a **disaster recovery envelope** — all the config, scripts, and secrets someone would need if this server died tomorrow.

## When to use

- User asks for a "recovery bundle", "restore kit", "bootstrap file", or "everything I need to rebuild you"
- After a server migration (document the new state)
- Before a destructive upgrade or decommission
- As a periodic safety net alongside regular backups

## What to include

### 1. Header section
- Title, date, one-line purpose
- "Paste this into a new Hermes to restore me" instruction

### 2. Bootstrap instructions
- Step-by-step: install Hermes, create each file, start gateway, recreate cron jobs, verify

### 3. Credentials summary (one place, easy to find)
- SMTP/IMAP password
- S3 (Wasabi/AWS) keys
- Cloud API tokens (Hetzner, netcup, etc.)
- VPN keys or passwords
- A clear note of what each credential is for
- **WARNING: The recovery bundle contains cleartext secrets.** It must be treated as a credential file and stored securely (password manager, encrypted vault). If emailed, use encrypted email or delete after transfer.

### 4. Config files — verbatim
- `~/.hermes/config.yaml`
- `~/.hermes/.env`
- `~/.aws/credentials`
- `~/.hermes/SOUL.md`
- `/etc/systemd/system/hermes.service`
- Any other env/config files the instance depends on

### 5. SSH keys (public + private)
- WISP keys, deploy keys, any SSH keys used by scripts

### 6. Cron jobs — full definition
Include for each job: name, schedule, script path, skills, mode (no_agent/agent), deliver target
Use `hermes cron list --json` (and fallback to text if JSON fails)

### 7. Scripts — verbatim
Every file in `~/.hermes/scripts/` that isn't in `__pycache__` or starts with `.`
Also include subdirectory scripts (wisp-backup/, etc.)

### 8. Today's conversation history
- Get today's user sessions (not cron sessions) from the state DB
- Filter by `started_at >= today_midnight AND id LIKE '20%'` (excludes cron sessions which start with `cron_`)
- Include full message text from both sessions
- Truncate extremely long messages (>5000 chars) with a note

## Key patterns and pitfalls

### Session filtering
Cron sessions have IDs like `cron_<hash>_<timestamp>` — filter them out by requiring `id LIKE '20%'` (user session IDs start with a date). The SQL filter:

```sql
SELECT id, title, started_at, ended_at, end_reason, message_count
FROM sessions 
WHERE started_at >= <today_unix_ts> AND id LIKE '20%'
ORDER BY started_at
```

### Handling SMTP delivery failure
The recovery bundle file can be large (200-500 KB). Common email pitfalls:

- **Your mail server and agent server are different machines** — always verify port reachability before assuming SMTP works
- Test each port separately: SMTP (587), IMAP (993). One can be open while the other is firewalled.
- A CNAME or MX record can point to a totally different provider than your other infrastructure. DNS lookup: `host mail.example.com` or `python3 -c "import socket; print(socket.gethostbyname('mail.example.com'))"`
- Test with: `python3 -c "import socket; s=socket.socket(); s.settimeout(5); s.connect(('<host>', 587)); print('OK')"`
- A port that **times out** (not refuses) points to a firewall drop, not a server-side block — your ISP or cloud provider may be filtering outbound SMTP
- If SMTP fails: try Wasabi S3 upload as fallback (`aws s3 cp` with `--endpoint-url`)
- Or deliver directly in the current chat (Telegram/WhatsApp can take large files)

### Don't fabricate
Do not include password/API-key values you "reconstruct" from memory. Only include credentials you actually read from files on this machine. If a credential file doesn't exist, say so rather than leaving a placeholder.

### Message truncation
Very long messages (compacted context, large JSON tool outputs) should be truncated at ~5000 chars with a note: `[truncated, was N chars]`. This keeps the bundle readable and under email size limits.

### Cron job retention from state.db
After a server migration, cron jobs survive in `state.db` but the scheduler needs ~30-60s to discover them after startup. If `hermes cron list` returns empty immediately after migration, wait and retry before assuming they were lost.

### Build script pattern
Rather than assembling the bundle inline in the agent's tool loop, write a Python build script (`scripts/build-recovery.py`) that:
1. Queries state.db for sessions/messages
2. Reads all config/script/credential files
3. Assembles the markdown document
4. Writes the output file

This avoids context-window overflow on large state DBs and makes the bundle reproducible.

## Verification

After building the recovery bundle:

1. Verify the file was written: `wc -c <path>`
2. Try to upload/send it (failures here are worth reporting to the user)
3. List what's in it: session count, script count, cron count
4. Report result to the user with the file location and any delivery issues

---
