Prompt: rotating encrypted backups on IPFS
Have an agent turn a folder or database dump into dated, encrypted
backups pinned on the ird IPFS gateway — encrypted on your machine to
your own key (or a password), so the network only ever holds ciphertext
— keep the last N, unpin the rest, and prove at least once that a
restore actually decrypts to the same bytes.
Needs: the ird CLI, logged in (encryption, signing and --decrypt are CLI-only; the MCP ipfs_add can encrypt but not sign, and cannot restore); local access to SOURCE; somewhere safe to back the key up
Costs: hourly per MB pinned for every backup kept, plus per MB served for restore downloads (credits_prices → recurring.ipfs-pin-hourly → per_mb_hour, per_mb_served)
Group: hosting
Paste everything below the line into a fresh conversation. Fill in the
FILL IN block first.
Task
Take one backup of SOURCE now, rotate older ones down to KEEP_LAST, run
a restore test, then give me the same as a cron script. Nothing is
uploaded before I confirm the size and cost.
FILL IN
APP: crm # pins are named backup:APP:<date>
SOURCE: ./data # folder to back up (or the file a dump command writes)
MODE: key # key = encrypt to my own irdpk1… key and sign | password
KEY_NAME: backups # local keypair name (key mode)
KEEP_LAST: 7 # backups that stay pinned
RESTORE_TEST: yes # download, decrypt and compare the backup just taken
Non-negotiable rules
- A lost key or password is lost backups. Content encrypted to a
lost key is gone forever. Private keys live under the CLI's config
directory (
ird ipfs keys --help names it) and never leave the
machine; before the first upload, I must confirm the key file is
backed up elsewhere. Never print the
private key. In password mode, never generate a password silently —
show it once and tell me to store it.
- Cost before uploading. Archive size ×
per_mb_hour × 730 ×
KEEP_LAST is the steady-state monthly cost; show it and wait for a
yes the first time. Over 1 GB per backup, ask every time.
- Rotation never goes below KEEP_LAST, never removes a pin whose
name is not
backup:APP:…, and never removes the newest backup
before its restore test passed.
- Folders can't be encrypted. Always archive first; the archive is
what gets encrypted and pinned. Encrypted pins are opaque blobs: no
browser viewing, only
ird ipfs get --decrypt.
Steps
A. Key (first time, key mode). ird ipfs keys ls — if KEY_NAME is
missing, ird ipfs keys gen --out KEY_NAME; it prints the public
irdpk1… identity. Keep that string for the script. Stop here until I confirm the key file is
backed up (password manager, offline media). By the flags, --encrypt-to
takes only the public string; --sign and --decrypt take a local key
name (private key needed).
B. Archive. DATE=$(date -u +%F);
tar czf backup-APP-DATE.tgz -C SOURCE .; sha256sum and du -h the
archive; compute rule 2 and wait.
C. Upload. Key mode: ird ipfs add backup-APP-DATE.tgz --encrypt-to
<irdpk1… of KEY_NAME> --sign --key KEY_NAME --name backup:APP:DATE --json.
Password mode: ird ipfs add backup-APP-DATE.tgz --encrypt --password
'<pw>' --name backup:APP:DATE --json (MCP: ipfs_add(path,
encrypt_password=…) or encrypt_to_public_key=…, unsigned). Encryption
happens locally; the command blocks until pinned; read the CID from its JSON (field
names undocumented — ird ipfs pins --json uses cid).
Delete the local archive afterwards unless I say otherwise.
D. Rotate. ird ipfs pins --search backup:APP: --all --json (MCP:
ipfs_pins(search="backup:APP:"), paging with offset). Names sort by
date; keep the newest KEEP_LAST entries in state pinned, list the
rest with CID and size, and after my yes (the first run — the script
does it unattended later) ird ipfs unpin <CID> each (MCP:
ipfs_unpin(pin=<CID>)). Billing stops at unpin. A failed pin is
unpinned too — it is not a backup.
E. Restore test (RESTORE_TEST, and after any key, machine or CLI
change): ird ipfs get <CID> --decrypt --key KEY_NAME -o restore.tgz
(password mode: --decrypt --password '<pw>'). In key mode the CLI
verifies the signature and prints the sender — it must be KEY_NAME's
irdpk1…. sha256sum restore.tgz must equal step B; tar tzf shows
real paths. Delete the copy. Downloads bill per MB served.
F. Script. Write backup.sh doing B–D with set -euo pipefail and
--json --yes --no-input, plus a cron line (e.g. 15 3 * * *). On a
server it authenticates with IRD_API_KEY from the environment or one
ird login --with-key; in key mode a server without the keypair drops --sign --key;
decrypt where the private key lives. An existing
backup:APP:DATE pin means today ran twice: skip the upload.
G. Cost recap. ird ipfs usage (MCP: ipfs_usage).
Deliverable
The irdpk1… public key (never the private one), the new backup's
name, CID and size, the restore-test result (checksums equal, sender
verified), the pins kept and unpinned with the monthly cost after
rotation, backup.sh and the cron line, and the sentence I must be able
to answer: where the key backup is.