A Go CLI that backs up every repo you can see on GitHub, wraps it in an age-encrypted archive, and uploads it to S3 — or anything S3-compatible. Tiny daily incrementals, a fresh full once a week. Signed and notarized on macOS; named configs for multiple profiles; runs on a schedule via launchd or systemd.
$ curl -fsSL https://dl.velaintelligence.com/elyndor/latest/install.sh | sh
Then: elyndor install · elyndor new · elyndor backup · elyndor service install --schedule @daily
User repos and any number of organizations. Public and private. Branches, tags, and full history. Glob include/exclude when you want a subset.
Opt-in age (scrypt passphrase) wraps either tar.gz or zip. The passphrase must be a ${NAME} reference resolved from env or the macOS Keychain — literals on disk are rejected.
AWS, Hetzner, MinIO, Wasabi, DigitalOcean Spaces, Cloudflare R2 — anything S3-compatible. Multipart upload, configurable storage class.
Worker pool clones in parallel. Exponential backoff with jitter on transient network errors. Atomic temp-then-rename — no half-cloned repos left behind.
Opt-in incremental: true: a fresh full archive once a week, daily diffs in between that ship only the repos whose HEAD changed. A 67-repo backup goes from ~1.2 GB nightly to a few hundred bytes when nothing changed. Chain-aware retention never deletes a full while its incrementals reference it.
One static binary, no external git required at runtime. The macOS build is signed with a Developer ID Application certificate and notarized by Apple — Gatekeeper accepts it on first run, no xattr workaround.
elyndor new work drops a new profile next to the default. Each config gets its own archive prefix, its own credentials, its own schedule — keep work, personal, and org backups isolated in one install.
elyndor service install --schedule @daily registers a launchd plist on macOS or a systemd user timer on Linux. Per-config. Logs land alongside each archive so a glance at the directory tells you what happened.
elyndor restore --name <config> reassembles the full plus every dependent incremental, layering files in order. For one-off use, elyndor decrypt still unwraps a single archive; or reach for age -d when you don't have Elyndor handy.
Custom endpoint + path-style addressing means anything that speaks S3 just works.