gec
is a command-line utility written in Bash with convenience commands for using gocryptfs with git.
It refrains from doing anything clever, making it possible to naively fallback to the underlying gocryptfs or git commands if a need should arise.
It transparently uses data encryption, both at rest and on the remotes. It uses version control and leverages redundant remote storage. The implemented remote commands require GitHub and Bitbucket. Git users will be at home with it.
⚠️ Before continuing, save the link to the official Gitee mirror of this repo.
- Screenshot
- Links
- Limitations
- Requirements
- Installation
- Development
- Setup
- Directories
- Workflow
- Commands
- Migration
Caption | Link |
---|---|
Repo | https://github.com/impredicative/gec |
Changelog | https://github.com/impredicative/gec/releases |
Mirror | https://gitee.com/impredicative/gec |
The known applicable size limits for GitHub and Bitbucket are tabulated below.
If a hard limit is violated during a commit
, gec
will attempt to check it and error early, otherwise a push
will fail.
Size of | SI Value | Type | Enforcer | Action by gec |
---|---|---|---|---|
File | 100M | Hard | GitHub | Error |
Push | 2G | Hard | GitHub | Error |
Repo | 5G | Soft | GitHub | (none) |
File | 1G | Hard | Bitbucket | (none) |
Push | 3.5G | Hard | Bitbucket | (none) |
Repo | 4G | Hard | Bitbucket | Error |
Due to the use of the gocryptfs -sharedstorage
option, a hardlink cannot be created in a decrypted repo.
Linux is required, along with a few tools which are covered in the Installation section.
A dedicated GitHub account and Bitbucket account is required with an identical username on both sites! If using Firefox, the Multi-Account Containers add-on can be very useful to separate them from your primary accounts on these sites.
These steps were tested on Ubuntu. On other distros, ensure that the executables are available in the PATH.
Ensure curl
≥7.74.0, git
≥2.25.1, jq
≥1.5:
sudo apt install curl git jq
Install gocryptfs
≥2.2.1:
# Install on amd64:
RELEASE=$(curl https://api.github.com/repos/rfjakob/gocryptfs/releases | jq -r .[0].tag_name)
wget -qO- https://github.com/rfjakob/gocryptfs/releases/download/${RELEASE}/gocryptfs_${RELEASE}_linux-static_amd64.tar.gz | sudo tar -xz -f - -C /usr/local/sbin/ gocryptfs gocryptfs-xray
# Install on arm64 on Ubuntu:
sudo apt install gocryptfs
Install git-sizer
≥1.3.0:
# Install on amd64:
VERSION=$(curl https://api.github.com/repos/github/git-sizer/releases | jq -r .[0].tag_name | tr -d v)
wget -qO- https://github.com/github/git-sizer/releases/download/v${VERSION}/git-sizer-${VERSION}-linux-amd64.zip | sudo busybox unzip - git-sizer -d /usr/local/sbin/
sudo chmod +x /usr/local/sbin/git-sizer
# Install on arm64 on Ubuntu:
sudo apt install git-sizer
Install gec
:
# Install program
RELEASE=$(curl https://api.github.com/repos/impredicative/gec/releases | jq -r .[0].tag_name)
sudo wget https://raw.githubusercontent.com/impredicative/gec/${RELEASE}/gec.sh -O /usr/local/sbin/gec
sudo chmod +x /usr/local/sbin/gec
# Install completion script if using Bash on Linux:
mkdir -p ~/.local/share/bash-completion/completions
wget https://raw.githubusercontent.com/impredicative/gec/${RELEASE}/completion.bash -O ~/.local/share/bash-completion/completions/gec
source ~/.local/share/bash-completion/completions/gec # This is automatic if bash-completion is installed, but can do manually for current terminal only.
# Install completion script if using Fish on Linux:
mkdir -p ~/.config/fish/completions
wget https://raw.githubusercontent.com/impredicative/gec/${RELEASE}/completion.fish -O ~/.config/fish/completions/gec.fish
source ~/.config/fish/completions/gec.fish # This is automatic, but can do manually for current terminal only.
For future updates to gec
, running gec install
will install its latest release.
This section is for gec
development only; it is not applicable for routine use.
# Clone repo
git clone git@github.com:impredicative/gec.git
cd ./gec
# Link program if on Linux
sudo ln -s "${PWD}/gec.sh" /usr/local/sbin/gec
In the steps below, <owner>
refers to an identical username in both GitHub and Bitbucket.
On each device:
- Run
gec config core.owner <owner>
once for all future repos. - Run
ssh-keygen -f ~/.ssh/id_gec
once to create a new SSH key. Optionally use and securely save a passphrase for this key to minimize the risk of any unauthorized push. - Add the
~/.ssh/id_gec.pub
file for the key created above into the<owner>
account in both GitHub and Bitbucket. - Create or prepend (not append) to
~/.ssh/config
the specific contents:Match host github.com,bitbucket.org exec "[[ $(git config user.name) = gec ]]" IdentityFile ~/.ssh/id_gec
- Run
chmod go-rw ~/.ssh/config
to tighten permissions of the file as is advised inman ssh_config
. - Run
gec test.ssh
to test GitHub and Bitbucket access via SSH, ensuring that the<owner>
name is printed for both. - Run
gec test.token
to test GitHub and Bitbucket access via a personal access token for each. The GitHub token must have access to therepo
anddelete_repo
scopes. The Bitbucket token must have access to therepository:read
,repository:admin
andrepository:delete
scopes.
Storage repos are created in ~/gec/
. This location is created automatically. Both encrypted and decrypted repos and their files are organized in this location.
Although this location is not currently configurable, a softlink or hardlink can be used to redirect it elsewhere if needed.
For each repo, these directories are created and used:
Location | Description |
---|---|
~/gec/encrypted/<repo> |
git repo contents |
~/gec/encrypted/<repo>/.git |
.git directory of git repo |
~/gec/encrypted/<repo>/fs |
encrypted filesystem contents within git repo |
~/gec/decrypted/<repo> |
decrypted filesystem mountpoint |
To create and provision a new repo:
gec init <repo>
gec use <repo>
touch .Trash-${UID}
(Avoids deleting files to Trash on Ubuntu)
To provision an existing repo:
gec clone <repo>
To use a provisioned repo, these are some of the many commands:
gec pull [<repo>]
(If and when changed on remote)gec use [<repo>]
(Remember toexit
the shell after using)gec status [<repo>]
gec done <repo> "a non-secret commit message"
(If files changed)gec umount <repo>
Refer to the repo-specific commands section for details on using the commands in the workflows above.
config <key> [<val>]
: Get or set a value of key from configuration file~/.gec
. To list all values, specify-l
.install [<release>]
: Update to the named or latest release ofgec
.list
: Alias ofls
.ls [pattern]
: List the output of thestate
command for matching repos in~/gec/encrypted
. If specifying a pattern, it may need to be quoted.lock
: Unmount all mounted repos.test.ssh
: Test access to GitHub and Bitbucket via SSH.test.token
: Test access to GitHub and Bitbucket via a personal access token for each.
In the commands below, <repo>
refers to an identical repository name, e.g. "travel-ak", in both GitHub and Bitbucket.
It can be auto-determined if a command is run from its encrypted or decrypted directory.
When it can be auto-determined, to disambiguate a command's arguments that follow, it can alternatively be specified as a period.
The minimally relevant repo-specific commands are listed in the Workflow section.
check.dec [<repo>]
: Check decrypted file sizes. Error if a size limit is exceeded. The repo must be in a mounted state. It is run automatically bycommit
when needed if mounted.check.git [<repo>]
: Check encrypted file sizes, and usegit-sizer
to check the size of the git repo. Error if a size limit is exceeded. It is run automatically bycommit
when needed.du [<repo>]
: Print the human-friendly disk usage of the git repo directory for a depth of one.du.dec [<repo>]
: Print the human-friendly disk usage of the decrypted directory for a depth of one. The repo must be in a mounted state.du.enc [<repo>]
: Print the human-friendly disk usage of the encrypted filesystem directory for a depth of one.info [<repo>]
: Alias ofstatus
.log [<repo>] [options]
: Print the git log for the last ten commits. Options, if any, are forwarded togit log
. If specifying any options, to auto-determine<repo>
, specify a period in its place.logs [<repo>]
: Alias oflog
.state [<repo>]
: Print the repo mount state, .git directory disk usage, encrypted filesystem directory disk usage, total disk usage, and repo name.status [<repo>]
: Print the repo name, mount state, and short git status. If mounted, also print the change status of decrypted paths plus the mount information.
A GitHub token and a Bitbucket token are required for these commands.
For your security, the tokens are not saved by gec
.
create <repo>
: Idempotently create the repo in GitHub and Bitbucket. The required GitHub token must have access to therepo
scope. The required Bitbucket token must have access to therepository:read
andrepository:admin
scopes.del [<repo>]
: Delete an existing repo in GitHub and Bitbucket. The required GitHub and Bitbucket tokens must have access to theirdelete_repo
andrepository:delete
scopes respectively. Also see therm
anddestroy
commands.
amend [<repo>] ["<commit_msg>"]
: Add and amend all changes to most recent commit. If<commit_msg>
is not specified, it is kept unchanged.<commit_msg>
is not encrypted. To auto-determine<repo>
, specify a period in its place.clone <repo>
: Clone and configure a preexisting repo from GitHub into its git repo directory, and add its Bitbucket URL.commit <repo> "<commit_msg>"
: Add and commit all changes.<commit_msg>
is not encrypted. To auto-determine<repo>
, specify a period in its place.gc [<repo>] [options]
: Run git garbage collection on the repo. Options, if any, are forwarded togit gc
. If specifying any options, to auto-determine<repo>
, specify a period in its place.pull [<repo>]
: Pull commits from remote. For safety, only a fast-forward pull is made, and a prerequisite is that the repo must be in a dismounted state.push [<repo>]
: Push commits to remote.reset.url [<repo>]
: Update the locally configured remote URLs for the repo to the expected ones for GitHub and Bitbucket, removing all other URLs.send <repo> "<commit_msg>"
: (commit
+push
) Add, commit, and push all changes.<commit_msg>
is not encrypted. To auto-determine<repo>
, specify a period in its place. Also see thedone
command.
dismount
: Alias ofumount
.init.fs [<repo>]
: Initialize the encrypted filesystem for an empty repo. No commit or push is made. A new password is requested. The password and a printed master key must be securely saved.mount [<repo>]
: Mount a repo in read-write mode into its decrypted mountpoint if not already mounted as such. Also see theuse
command.mount.ro [<repo>]
: Mount a repo in read-only mode into its decrypted mountpoint if not already mounted as such. Also see theuse.ro
command.mount.rw
: Alias ofmount
. Also see theuse.rw
command.umount [<repo>] [-f]
: Unmount a repo if it is mounted. To force an unmount, specify-f
.unmount
: Alias ofumount
.
rm <repo>
: Interactively remove all local directories of the repo. Also see thedel
anddestroy
commands.shell [<repo>]
: Provide a shell into the git repo directory.shell.dec [<repo>]
: Provide a shell into the decrypted mountpoint of a mounted repo.shell.enc [<repo>]
: Provide a shell into the encrypted filesystem directory.
init <repo>
: (create
+clone
+init.fs
+send
) Create new repo remotely, clone it locally, initialize encrypted filesystem, commit, and push. A new password is requested. The password and a printed master key must be securely saved. The required GitHub token must have access to therepo
scope. The required Bitbucket token must have access to therepository:read
andrepository:admin
scopes.destroy <repo>
: (rm
+del
) Interactively remove all local repo directories, and delete repo from GitHub and Bitbucket.done <repo> "<commit_msg>"
: (check.dec
+umount
+send
) Add, commit, and push all changes. Unmount repo if mounted.<commit_msg>
is not encrypted. To auto-determine<repo>
, specify a period in its place.rename <repo> <new_name>
: Rename repo remotely and locally, and update its locally configured remotes. The repo must be in a dismounted state. The new name must not already be in use. The required GitHub and Bitbucket tokens must have access to theirrepo
andrepository:admin
scopes respectively. After renaming, remember to store the password for the repo under its new name.use [<repo>]
: (mount
+shell.dec
) Mount read-write if not already mounted as such, and provide a shell into the decrypted mountpoint.use.ro [<repo>]
: (mount.ro
+shell.dec
) Mount read-only if not already mounted as such, and provide a shell into the decrypted mountpoint.use.rw
: Alias ofuse
.
If updating from v0.4, first verify the Setup steps.
Next, for each repo:
- If it exists remotely but not locally, obtain it using
gec clone <repo>
. - Check its repo size as printed by
gec check.git <repo>
. - If the repo size is ≤4.0G, run this sequence once:
gec shell <repo> # Enters repo shell gec create # Creates repo in Bitbucket. gec reset.url # Adds Bitbucket remote and removes obsolete remote. gec push # Pushes to Bitbucket. exit # Exits repo shell.
- If
gec push
fails with the push size exceeding 2G, run an alternative command instead such as the one below to push commits individually. At this time, ignore errors about failing to push commits to GitHub.git log --format=%H --reverse | xargs -i git push origin {}:refs/heads/$(git branch --show-current)
- If the repo size is >4.0G, it cannot be migrated. One or more new repos can however be created to replace it. In each new repo, limit each push to a max of 2G.