Reference
Setup & pairing
The entire UX is one instruction from the user to their agent:
# user tells Hermes Agent:
Set up cloud backups for yourself using hermesbackup.com
- The agent fetches
https://hermesbackup.com/agent(mirrored at/llms.txt) for exact install instructions. - The skill's
setupcallsPOST /v1/pair/start(unauthenticated) with the device hostname → receives a pairing code (FROG-8241) and poll token. - The agent tells the user: “Go to hermesbackup.com/pair and enter code FROG-8241” (sign-up via Clerk happens in under a minute if no account exists).
- The script polls
POST /v1/pair/pollevery 5 seconds (10-minute expiry). On approval it receives a device token, stored chmod 600. - Setup generates the age keypair, prints the recovery kit, asks about schedule (default: daily
--quickat 03:00 local + weekly full on Sundays) andon_fullpolicy, writes cron entries, runs a first backup immediately, and confirms end-to-end success.
Authorization: Bearer <de...de>. The dashboard uses Clerk JWT. Agents never touch Clerk.API reference
Base URL: https://api.hermesbackup.com/v1. JSON everywhere. Errors as {"error": {"code", "message"}}.
Pairing
Auth: none. Body:
{device_name}. Returns {pair_code, poll_token, expires_at}.Auth: none. Body:
{poll_token}. Returns 202 pending or {device_token, device_id} once approved.Auth: Clerk. Dashboard-only — agent never calls this. Approves
{pair_code} for the signed-in user.Backups
Auth: device. Body:
{size_bytes, sha256, label, kind} (kind: full|quick). Checks quota + rate limits. Returns {backup_id, upload_url} or 507.Auth: device. Server HEADs the R2 object, verifies size + SHA-256 checksum, marks the row
stored, updates usage.Auth: device or Clerk. List backups: id, label, kind, size, sha256, created_at, device.
Auth: device or Clerk. Returns
{download_url} (presigned GET, 5-min expiry).Auth: device or Clerk. Delete object + row; tombstone retained 7 days.
Account
Auth: device or Clerk. Returns
{plan, used_bytes, limit_bytes, object_count}.Auth: Clerk. List devices: name, id, paired-date, last-backup, health, revoked.
Auth: Clerk. Revoke device. Existing backups are untouched.
Auth: signature. Billing state changes.
Auth: signature. Email bounce handling.
init with no complete within 24 hours) are garbage-collected by a cron trigger.D1 schema (abridged)
users(id TEXT PK, clerk_id TEXT UNIQUE, email TEXT, plan TEXT DEFAULT 'free', stripe_customer_id TEXT, used_bytes INTEGER DEFAULT 0, limit_bytes INTEGER DEFAULT 2147483648, created_at INTEGER); devices(id TEXT PK, user_id TEXT REFS users, name TEXT, token_hash TEXT UNIQUE, last_backup_at INTEGER, expected_interval_hours INTEGER DEFAULT 24, revoked INTEGER DEFAULT 0, created_at INTEGER); backups(id TEXT PK, user_id TEXT, device_id TEXT, r2_key TEXT UNIQUE, label TEXT, kind TEXT, size_bytes INTEGER, sha256 TEXT, status TEXT DEFAULT 'pending', -- pending|stored|deleted created_at INTEGER, deleted_at INTEGER); pairings(pair_code TEXT PK, poll_token_hash TEXT UNIQUE, device_name TEXT, user_id TEXT, status TEXT DEFAULT 'pending', expires_at INTEGER);
Dashboard
Served from Worker static assets at hermesbackup.com. Auth: Clerk JWT via middleware. Built as a small React app.
- Backups — list, download (presigned GET), delete, usage meter with 4 states (normal / warning / critical / full).
- Devices — pairing approval inline, revoke with confirmation, last-seen and health badges (healthy / silent / overdue / revoked).
- Billing — plan status, Stripe Customer Portal deeplink (we never render a card-number field), planned tiers as read-only placeholders.
- Settings — email prefs, delete account (purges all objects).
Full dashboard UX spec lives at design/dashboard-ux-spec.md in the repo. Interactive prototype at design/dashboard-prototype.html.
Restore on a new machine
# on the new machine, after installing Hermes Agent: hermesbackup.sh restore # flow: No device paired. Starting pairing flow... Pair code: OTTER-3091 … Select backup to restore [1-3]: 1 Downloading ciphertext via presigned URL... Decrypting with age identity... Enter AGE-SECRET-KEY-1: ********** ✓ SHA-256 verified: 7f2a...e910 matches server record ✓ Plaintext written to /tmp/hermes-restore-XXXX/ Importing to ~/.hermes/... ✓ 4,579 files imported. SOUL.md, MEMORY.md, skills/, sessions/ restored. # done. Your agent remembers everything.
Retention & quota
Backups accumulate until the account hits its quota (2 GB free / 10 GB Pro). When an init would exceed quota, the API returns 507 INSUFFICIENT_STORAGE with current usage. The skill then follows its configured policy:
on_full = "fail"(default): abort, log, server emails the user.on_full = "rotate": skill deletes the oldest backups via the API until the new one fits, then retries. The user opts into this at setup; it's automatic pruning by client choice, so the server never silently destroys data.
Deletes are immediate (object + row) with a 7-day tombstone record for support disputes. Quota warnings email at 80% and 95%. The server never silently deletes user data — invariant.
Email plan
Transactional only, via Emailit REST v2 with webhook-driven bounce suppression. Pointedly: the recovery-kit reminder does not contain the key.
- Welcome + recovery-kit reminder
- Device-paired confirmation
- First-backup success
- Backup-failure alert
- Missed-schedule nudge (device silent > 2× expected interval)
- Quota 80% / 95%
- Payment failed (dunning ×3) / renewal receipt
- Account-deletion confirmation
Estimated volume: ~15 emails per user per year — inside the free 1,000/month until roughly 800 active users.
Unit economics
Per user per year:
| Item | Free user | Pro (avg 4 GB) | Pro (10 GB worst case) |
|---|---|---|---|
| Revenue | $0 | $10.00 | $10.00 |
| Stripe fee (2.9% + $0.30) | — | −$0.59 | −$0.59 |
| R2 storage ($0.015/GB-mo) | −$0.18 | −$0.72 | −$1.80 |
| R2 egress | $0 | $0 | $0 |
| Workers / D1 / KV marginal | ~$0.01 | ~$0.01 | ~$0.01 |
| Net contribution | −$0.20 | +$8.67 | +$7.59 |
R2's zero egress is why this business works at $10/year. A 10 GB restore on S3 would cost ~$0.90 — nearly 10% of annual revenue gone in one click. On R2 it costs us nothing.
Fixed cost floor: ~$75–100/year (Workers Paid $60, domain ~$10, Emailit ~$0–20). Break-even at roughly 12 Pro subscribers. Full scenario projections are in the spec at files/hermesbackup-spec.md §11.