GIT-REMOTE-GCRYPT(1)
π GIT-REMOTE-GCRYPT(1) π
okay bestie, so this is the manual page for git-remote-gcrypt, which is, and I quote, the GNU Privacy Guard-encrypted git remote. that's the whole title. now sit down, grab a snack π, and let's get into it.
β¨ NAME β¨
git-remote-gcrypt β GNU Privacy Guard-encrypted git remote π
πΈ DESCRIPTION πΈ
git-remote-gcrypt is a git remote helper, and its whole thing is pushing to and pulling from repositories that are encrypted with GnuPG, using a custom format. it handles any URI that starts with gcrypt::. that's the vibe. π«Ά
So the supported backends are local, rsync:// and sftp://, where your repo just lives on disk as a set of files β OR, alternatively, any <giturl>, where gcrypt stores that exact same representation inside a git repository, bridged over whatever arbitrary git transport you like. if you can use local or rsync://, please do, bestie; there's a whole discussion of why in "Performance" further down. π
There's also an experimental rclone:// backend, but it is for early adoptors only. you have been warned. π
The aim of the whole thing? confidential, authenticated git storage and collaboration, using typical untrusted file hosts or services. iconic honestly. π
π Quickstart π
You create an encrypted remote by pushing to it. literally that's it:
git remote add cryptremote gcrypt::rsync://example.com/repo
git push cryptremote master
> gcrypt: Setting up new repository
> gcrypt: Remote ID is :id:7VigUnLVYVtZx8oir34R
> [ more lines .. ]
> To gcrypt::[...]
> * [new branch] master -> master
π CONFIGURATION π
here are all the git-config(1) variables that are supported, no cap:
remote.<name>.gcrypt-participants,gcrypt.participantsβ a space-separated list of GPG key identifiers. the remote gets encrypted to these participants, and only signatures from these participants are accepted. also psst:gpg -klists all public keys you know. If this option isn't set, we encrypt to your default key and accept any valid signature. you can also ask for that behaviour explicitly by setting participants tosimple. heads up: thegcrypt-participantssetting on the remote takes precedence over the repository variablegcrypt.participants.remote.<name>.gcrypt-publish-participants,gcrypt.publish-participantsβ by default, the gpg key ids of the participants are obscured by encrypting usinggpg -R. setting this option totruedisables that security measure. β οΈ the catch withgpg -Ris that to decrypt, gpg tries each available secret key in turn until it finds a usable one, which can result in unnecessary passphrase prompts. so it's a tradeoff, iykyk.gcrypt.gpg-argsβ whatever's in this setting gets passed as arguments to gpg. e.g.--use-agent.remote.<name>.gcrypt-signingkey,user.signingkeyβ (that second one comes from your regular git configuration.) it's the key to use for signing. you should setuser.signingkeyif your default signing key isn't part of the participant list. and you can use the per-remote version to sign different remotes with different keys. multi-key main character energy. πremote.<name>.gcrypt-rsync-put-flags,gcrypt.rsync-put-flagsβ flags to pass to rsync when uploading to a remote over thersync://backend. if the flags are set for a specific remote, then the global flags β even if they're also set β will not be applied for that remote.remote.<name>.gcrypt-require-explicit-force-push,gcrypt.require-explicit-force-pushβ okay so there's a longstanding bug where every single git push effectively has a--force. if this flag is set totrue,git-remote-gcryptwill refuse to push unless--forceis passed, or unless the refspecs are prefixed with+.
π ENVIRONMENT VARIABLES π
GCRYPT_FULL_REPACKβ when this is set (to anything at all, doesn't matter what), it forces a full repack when pushing. π₯
π¦ EXAMPLES π¦
Setting up a remote for two participants:
git remote add cryptremote gcrypt::rsync://example.com/repo
git config remote.cryptremote.gcrypt-participants "KEY1 KEY2"
git push cryptremote master
Using a git backend:
# notice that the target git repo must already exist and its
# `next` branch will be overwritten!
git remote add gitcrypt gcrypt::git@example.com:repo#next
git push gitcrypt master
the URL fragment (#next in this case) tells you which backend branch is being used. π―
π NOTES π
π« Collaboration π«
the encryption of the manifest gets updated on every push so it matches the participant configuration. which means every pushing user needs to have the public keys of all collaborators and the correct participant config. no freeloading.
π« Dependencies π«
rsync, curl and rclone are needed for the rsync:, sftp: and rclone: remotes respectively. and the main executable requires a POSIX-compliant shell that supports local.
π« GNU Privacy Guard π«
both GPG 1.4 and 2 are supported. you need a personal GPG key. GPG configuration applies to algorithm choices for public-key encryption, symmetric encryption, and signing. run man gpg if you want more. π
π« Remote ID π«
the Remote ID is not secret. it only exists so that two repositories signed by the same user can be told apart. you'll get a warning if the Remote ID changes, which should only happen if the remote was re-created. so like, if you see that warning, something happened. π
π« Performance π«
this is the part where i hold your hand. π«Ά using an arbitrary <giturl> or an sftp:// URI requires uploading the entire repository history with every push. which means pushes get slower over time as your git history grows, and it can easily get to the point where continued usage of git-remote-gcrypt is just impractical.
so: use these backends only when you know your repository will never grow very large β not just that it isn't large right now. that makes these backends inappropriate for most repositories, and probably only suitable for unusual cases, like small credential stores. even then? use rsync:// if you can. but note that rsync:// won't work with a repository hosting service like Gitolite, GitHub or GitLab. π
π« rsync URIs π«
the URI format for the rsync backend is rsync://user@host/path, which translates to the rsync location user@host:/path, accessed over ssh. note that the path is absolute, not relative to the home directory. an earlier non-standard URI format is also supported: rsync://user@host:path, which translates to the rsync location user@host:path.
π« rclone backend π«
besides adding the rclone backend as a remote with a URI like gcrypt::rclone://remote:subdir, you also have to add the remote to the rclone configuration itself. usually that's done by running rclone config. see rclone(1). β¨
the rclone backend is considered experimental and is for early adoptors only. you have been warned. π
π§ Repository format π§
here's the notation, keep it in your notes:
EncSign(X): Sign and Encrypt to GPG key holder
Encrypt(K,X): Encrypt using symmetric-key algorithm
Hash(X): SHA-2/256
B: branch list
L: list of the hash (Hi) and key (Ki) for each packfile
R: Remote ID
To write the repository:
Store each packfile P as Encrypt(Ki, P) β P' in filename Hi
where Ki is a new random string and Hash(P') β Hi
Store EncSign(B || L || R) in the manifest
To read the repository:
Get manifest, decrypt and verify using GPG keyring β (B, L, R)
Warn if R does not match previously seen Remote ID
for each Hi, Ki in L:
Get file Hi from the server β P'
Verify Hash(P') matches Hi
Decrypt P' using Ki β P then open P with git
π§ Manifest file π§
here's an example manifest file (with ellipsis for brevity):
$ gpg -d 91bd0c092128cf2e60e1a608c31e92caf1f9c1595f83f2890ef17c0e4881aa0a
542051c7cd152644e4995bda63cc3ddffd635958 refs/heads/next
3c9e76484c7596eff70b21cbe58408b2774bedad refs/heads/master
pack :SHA256:f2ad50316...cd4ba67092dc4 z8YoAnFpMlW...3PkI2mND49P1qm
pack :SHA256:a6e17bb4c...426492f379584 82+k2cbiUn7...dgXfyX6wXGpvVa
keep :SHA256:f2ad50316...cd4ba67092dc4 1
repo :id:OYiSleGirtLubEVqJpFF
each item extends until newline, and matches one of the following:
<sha-1> <gitref>β the git object id and its refpack :<hashtype>:<hash> <key>β the packfile hash (Hi) and its corresponding symmetric key (Ki).keep :<hashtype>:<hash> <generation>β the packfile hash and its repack generationrepo <id>β the remote idextn <name> ...β extension field, preserved but unused.
π DETECTING GCRYPT REPOS π
to detect if a git url is a gcrypt repo, use: git-remote-gcrypt --check url
exit status is 0 if the repo exists and can be decrypted, 1 if the repo uses gcrypt but could not be decrypted, and 100 if the repo is not encrypted with gcrypt (or could not be accessed).
note that this has to fetch the repo contents into the local git repository, same as what happens when you use a gcrypt repo. π
π KNOWN ISSUES π
- Every git push effectively has
--force. be sure to pull before pushing. bestie please pull first. π git-remote-gcryptcan decide to repack the remote without warning, which means your push can suddenly take significantly longer than you were expecting, because your whole history has to be reuploaded. and that push might fail over a poor link.git-remote-gcryptmight report a repository as "not found" when the repository does in fact exist, butgit-remote-gcryptis having authentication, port, or network connectivity issues. so don't panic immediately.
β SEE ALSO β
git-remote-helpers(1), gpg(1)
π CREDITS π
- the original author of
git-remote-gcryptwas GitHub user bluss. - the de facto maintainer in 2013 and 2014 was Joey Hess.
- the current maintainer, since 2016, is Sean Whitton
<spwhitton@spwhitton.name>.
πΈ LICENSE πΈ
this document and git-remote-gcrypt are licensed under identical terms, GPL-3 (or 2+); see the git-remote-gcrypt file.
okay that's the whole page, bestie. π this was GIT-REMOTE-GCRYPT(1), GNU Privacy Guard-encrypted git remote, and that's the page header and footer right there β no version or date given on this one, just the name. slay, go encrypt something, and remember: pull before you push. periodt. π
β¨π¦
π¦ where this page comes from
- debian bookworm: git-remote-gcrypt 1.5-1 Β· GPL-2.0-or-later
- debian trixie: git-remote-gcrypt 1.5-1 Β· GPL-2.0-or-later
- ubuntu noble: git-remote-gcrypt 1.5-1 Β· GPL-2.0-or-later
- ubuntu questing: git-remote-gcrypt 1.5-1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms