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 -k lists 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 to simple. heads up: the gcrypt-participants setting on the remote takes precedence over the repository variable gcrypt.participants.
  • remote.<name>.gcrypt-publish-participants, gcrypt.publish-participants β€” by default, the gpg key ids of the participants are obscured by encrypting using gpg -R. setting this option to true disables that security measure. ⚠️ the catch with gpg -R is 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 set user.signingkey if 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 the rsync:// 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 to true, git-remote-gcrypt will refuse to push unless --force is 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 ref
  • pack :<hashtype>:<hash> <key> β€” the packfile hash (Hi) and its corresponding symmetric key (Ki).
  • keep :<hashtype>:<hash> <generation> β€” the packfile hash and its repack generation
  • repo <id> β€” the remote id
  • extn <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-gcrypt can 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-gcrypt might report a repository as "not found" when the repository does in fact exist, but git-remote-gcrypt is 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-gcrypt was 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