We cannot use a fork to back up a client's private repository. The fork can be destroyed by the other party at any time, with no warning and no action on our part.
Importance: High - the failure mode is silent and total: complete loss of client IP we believed was safe.
Urgency: Medium - no active incident, but the pattern recurs on every vendor handover.
Priority: P2
GitHub deletes the private forks of a private repository when the parent repository is deleted. It also deletes a collaborator's forks when their access to the parent is revoked. Either event is entirely under the other party's control.
This nearly cost us the Curia Regis source. Three forks were held in the client org as the backup of an outgoing vendor's code, while that vendor had announced a deletion date. Had they revoked access even a day early, the forks would have gone with it.
Solution
Standard: client code is preserved as an independent mirror, never a fork.
git clone --mirror https://github.com/SOURCE_ORG/repo.git repo.git
gh repo create OUR_ORG/repo-mirror --private
git -C repo.git push --mirror https://github.com/OUR_ORG/repo-mirror.git
A mirror carries every branch, tag and note, is owned solely by us, and has no upstream link that the other party can sever. Re-running the fetch and push keeps it current for as long as our read access lasts.
Guard rail: never point push --mirror at a working repository. It deletes any remote ref missing locally.
Working reference implementation: scripts/mirror-repos.sh in holdex/client-curia-regis.
References
We cannot use a fork to back up a client's private repository. The fork can be destroyed by the other party at any time, with no warning and no action on our part.
Importance: High - the failure mode is silent and total: complete loss of client IP we believed was safe.
Urgency: Medium - no active incident, but the pattern recurs on every vendor handover.
Priority: P2
GitHub deletes the private forks of a private repository when the parent repository is deleted. It also deletes a collaborator's forks when their access to the parent is revoked. Either event is entirely under the other party's control.
This nearly cost us the Curia Regis source. Three forks were held in the client org as the backup of an outgoing vendor's code, while that vendor had announced a deletion date. Had they revoked access even a day early, the forks would have gone with it.
Solution
Standard: client code is preserved as an independent mirror, never a fork.
A mirror carries every branch, tag and note, is owned solely by us, and has no upstream link that the other party can sever. Re-running the fetch and push keeps it current for as long as our read access lasts.
Guard rail: never point
push --mirrorat a working repository. It deletes any remote ref missing locally.Working reference implementation:
scripts/mirror-repos.shin holdex/client-curia-regis.References