parked_sketch_r2_offsite_mirror_rsyncnet_repoint_2026-06-14
DARE.CO.UK · PARKED SKETCH · 2026-06-17
Mirrored from ~/.claude/.../memory/parked_sketch_r2_offsite_mirror_rsyncnet_repoint_2026-06-14.md. This is a design sketch parked for future build — read for context, not as a current deliverable.
“Plan to move the R2 off-Cloudflare durability mirror from Wasabi SG to rsync.net, then delete gf-cx-backup-sg to halve Wasabi cost”
Decision (Dan 2026-06-14): kill the Wasabi SG backup bucket
gf-cx-backup-sg and move the off-Cloudflare R2 durability mirror to
rsync.net (hk1187, HK) — which is already paid (1 TB .edu annual) so the
backup costs $0 marginal, gains HK geo + ZFS-snapshot immutability, and drops
Wasabi to ONE region (gf-cx-archive, us-east-1, noir/content) = ~$6.99/mo
instead of $13.98. Floor is per-REGION not per-bucket, which is why this works.
Context: the new status.gf.cx/wasabi page (gfcx_wasabi_storage_snapshot.py)
surfaced the 2× floor; see [[parked_sketch_wasabi_storage_status_page_2026-06-14]].
RESOLVE FIRST (gating question) — does the existing restic → rsync.net “parachute” repo already cover the irreplaceable R2 corpora? If YES, just delete SG, no new mirror needed. If NO (likely it backs up LOCAL Mac data, not the R2 buckets), add an rclone sftp target. Check what the restic harness backs up before building anything.
Current state (verified 2026-06-14):
- Mirror = ~/bin/r2_wasabi_mirror.py: env-var rclone remotes (NO on-disk
secrets) r2: (1P R2 backup readonly access_key_id/secret_access_key) and
wasabi: (1P Wasabi api). WASABI_BUCKET="gf-cx-backup-sg",
WASABI_REGION=ap-southeast-1. dst = wasabi:gf-cx-backup-sg/r2/<bucket>.
TIER1 = irreplaceable (pa-amazon-email-evidence, pa-ebay-email-evidence,
pa-evernote-substrate, gf-cx-media, …); TIER2 = derivable. Runs via
r2_wasabi_mirror_run.sh (shlock single-flight) on launchd
cx.gf.r2-wasabi-mirror at 10:00 & 18:00 ET. Health = r2_wasabi_mirror_check.py
(--status-json --pushover; stamps ~/.r2_wasabi_mirror/last_sync.json).
Script says NOT to run the seed on the home Mac (Fios ~8.5 MB/s upload
ceiling) — cloud-to-cloud, prefer a burst Linode (see linode_burst.py).
- SG bucket holds: r2/<bucket>/… (the mirror), happiness/video-source/,
_internal/created.txt. ~24 GB once the in-progress seed finishes.
- rsync.net: hk1187.rsync.net, key ~/.ssh/rsync_net_ed25519, alias in
~/.ssh/config (rsync.net-hk). 1P item rsync.net restic
(id waplm4dtxxpzygnvw2jczo43hi). NO rclone sftp remote exists yet.
Execution plan (post-compact, fresh context):
1. Confirm restic-coverage gating question above.
2. If a new mirror is needed: add an rclone rsyncnet: sftp remote via
env-var injection in r2_wasabi_mirror.py —
RCLONE_CONFIG_RSYNCNET_TYPE=sftp, _HOST=hk1187.rsync.net,
_USER=<rsync.net acct, per ~/.ssh/config>,
_KEY_FILE=~/.ssh/rsync_net_ed25519. Switch dst to
rsyncnet:r2-mirror/<bucket> (plain FS path over sftp, not S3). Generalize
naming (r2_wasabi_mirror → r2_offsite_mirror) OR keep filename + retarget.
Update r2_wasabi_mirror_check.py parity to list over sftp (not S3
object-count), and the run wrapper. Don’t leave the twice-daily job
half-edited.
3. Seed r2_*_mirror.py --tier all --apply — ideally on a burst Linode
(cloud→rsync.net), or accept the slow home run once.
4. Verify parity (object/file counts + sizes both sides) BEFORE any delete.
5. Move happiness/video-source/ + anything non-r2/ off SG too.
6. DELETE gf-cx-backup-sg (aws s3 rm –recursive then rb, Wasabi
ap-southeast-1 endpoint). Wasabi page will then show 1 region / ~$6.99/mo.
7. Re-point/retitle the r2-wasabi-mirror status card + dare_daily_hygiene
check; the gfcx_wasabi_storage_snapshot.py page auto-reflects 1 region.
Tracked as session task #3.