Back up GitHub and GitLab to storage you lock. Then prove it restores.
Restores the copy.
Compares every ref.
Signs the result.
No human in the loop.
go install gitdr.io/gitdr/cmd/gitdr@latestOne job, five steps, fail closed.
Enumerate
Every repo, over read-only scopes.
Verify WORM
Object lock, asked before every write. A change shows up on the next run.
Bundle
Mirror clone plus LFS, one bundle, SHA-256.
Upload
Create-only, with a retain-until date.
Sign
One ed25519 manifest over the run.
Run the drill yourself.
$ gitdr drill --config gitdr.yaml --manifest <key> --output json
{
"schema": "gitdr.drill/v1",
"manifestSigned": true,
"status": "success",
"eligible": 214,
"drilled": 214,
"repos": [
{
"slug": "acme/api",
"status": "success",
"sourceRefs": 84,
"bundleRefs": 84,
"restoredRefs": 20,
"unreferenced": [
"refs/merge-requests/1/head",
"refs/notes/commits"
],
"sourceMatch": true
}
]
}84 refs declared, 20 restored, 64 counted apart.
A clone creates no ref for refs/merge-requests/*, so gitdr names them rather than folding them in.
It restores to the history it declares
A fresh clone, against the bundle’s own header.
It declares the history the source had
Against the ref map the manifest signed.
How it compares
Git is content addressed, so a matching commit hash covers its tree, its blobs and its whole ancestry. This is a proof of equality, not a sample.
sourceMatch is absent, not false, when the manifest predates source-ref recording. Missing is not passing.
There is no delete method.
No remove, and no overwrite.Grep the destination packages. A test walks them on every build and fails on a delete call, whatever the method is named.
Who can still delete this?
Holds against everyone
Object Lock in compliance mode.
Or a retention policy you locked.
Not even the account root.
- Amazon S3
- Google Cloud Storage
- Azure Blob
- Backblaze B2
- Wasabi
Holds if you trust whoever holds the disks
The lock stops the S3 API.
It does not stop root on the host.
- MinIO
- Ceph / RGW
Holds unless an administrator removes it
Bucket locks, not Object Lock.
They stop deletes and overwrites.
gitdr cannot read them.
- Cloudflare R2
You turn the lock on, not gitdr.
--require-worm stops the run rather than writing to a bucket it could not confirm.
gitdr reads the configuration, not the enforcement.
GitHub and GitLab, read-only.
- GitHub
- GitHub Enterprise Server
- GitLab
- GitLab self-managed
A GitHub App, or a GitLab group token. Every repository, its LFS objects, and a JSON file of issues, pull and merge requests, releases and labels.
It writes only what changed.
It compares refs, not dates
One git ls-remote against the ref map the last manifest signed. A deleted branch counts as a change.
A duplicate on locked storage is permanent
Compliance mode holds against you as well, so a copy you did not need is one you cannot delete.
Every stored bundle is a complete history.
It restores on its own, chained to nothing.
Nothing is skipped past a third of its retention.
Ship it three ways.
Go or a release download
$ go install gitdr.io/gitdr/cmd/gitdr@latestStatic, Linux amd64 and arm64. Also on the GitHub releases page with checksums and signatures.
Hardened image
$ docker run ghcr.io/gitdr-io/gitdr backup --config gitdr.yamlWolfi base, non-root, read-only rootfs, no shell. Ships git and git-lfs, signed with cosign.
Helm CronJob
$ helm install gitdr oci://ghcr.io/gitdr-io/charts/gitdrRuns on a schedule, writes a last-successful-run metric your alerting can watch.