Skip to content

Repository files navigation

Galacrypt

Alternative to transcrypt with simpler mental model and 0 dependencies

Why Galacrypt ?

  • Pushing .env files is just not for me (transcrypt encrypt/decrypt .env files on the fly before commiting)
  • I think commiting .env adds a mental charge of making sure transcrypt is well configured before pushing your .env (counter intuitive action)
  • In fact I think that transcrypt way "looks like a hack", what is done differs from what you see: e.g.: you see a clear .env, but in the git file, it's encrypted.

TLDR: When dealing with secrets I prefer WYSIWYG

What does galacrypt do?

In your config you define a set of input/output files

{
  "files": [
    {
      "input": ".env",
      "output": ".env.galacrypt"
    }
  ]
}

On commit (or when running yarn galacrypt encrypt) it will encrypt those files using an AES 256 key, stored in a file .galacryptkey

On git pull, git rebase (or when running yarn galacrypt decrypt) it will decrypt those files

💡 With this approach, you commit the ouput files but keep the input files gitignored. What you see encrypted will be encrypted, what you see in clear text will stay in clear text (and should be gitignored)

When you git clone the project, after setting the AES key with

yarn galacrypt use <key>

You can restore your files by running

yarn galacrypt decrypt --all

Installation

yarn add -D @galadrim/galacrypt

Setup

Setup for a new project

Run yarn galacrypt init

this will do the following things:

  • add the .galacryptkey to your .gitignore
  • create a galacrypt key, stored in .galacryptkey (you must gitignore this file and save the key somewhere safe e.g. your password manager)
  • create a pre-commit hook that will run galacrypt encrypt
  • create a post-rewrite hook (e.g. after git rebase) that will run galacrypt decrypt
  • create a post-merge hook (e.g. after git pull) that will run galacrypt decrypt
  • create a .galacryptrc.json file, you must then edit it to your needs (see the json example below)

To finish your configuration, add some files to be encrypted in the .galacryptrc.json file here is the format :

interface GalacryptConfig {
  files: {
    input: string;
    output: string;
    // keep scrolling to see details about these options
    disableImplicitDecrypt?: boolean;
    mode?: 'file' | '.env';
  }[];
}

and an example:

{
  "files": [
    {
      "input": ".env",
      "output": ".env.galacrypt"
    }
  ]
}

⚠️ Before leaving this page, please double check that the input files are in your .gitignore file

Setup for an existing project

Ask your team for the galacrypt key of your project

Then use this command:

yarn galacrypt use <key>

This will do the following things:

  • put the provided galacrypt key inside .galacryptkey
  • create a pre-commit hook that will run galacrypt encrypt
  • create a post-rewrite hook (e.g. git rebase) that will run galacrypt decrypt
  • create a post-merge hook (e.g. git pull) that will run galacrypt decrypt

Package Manager Configuration

By default, Galacrypt uses yarn to run the git hooks. You can override this behavior by using one of the following flags when running galacrypt init or galacrypt use <key>:

  • --yarn: Use yarn (default)
  • --pnpm: Use pnpm
  • --npm: Use npm
  • --bun: Use bun
  • --package-manager <command>: Use a custom package manager command

Example:

# Using pnpm
galacrypt init --pnpm

# Using a custom command
galacrypt use <key> --package-manager "deno"

Usage

# to setup galacrypt on a new project (will generate a new AES 256 key)
yarn galacrypt init
# to setup galacrypt on an existing project
yarn galacrypt use <key>
# to encrypt input files set in .galacryptrc.json to their output versions
yarn galacrypt encrypt
# to decrypt output files set in .galacryptrc.json to their input versions
# note that it won't decrypt files with `disableImplicitDecrypt: true`
yarn galacrypt decrypt
# to decrypt all output files set in .galacryptrc.json to their input versions
yarn galacrypt decrypt --all
# to encrypt only some files (coma separated list of input files)
yarn galacrypt encrypt --only .env
# to decrypt only some files (coma separated list of output files)
yarn galacrypt decrypt --only .env.galacrypt
# to git add output files after encryption (used in the pre-commit hook)
yarn galacrypt encrypt --git-add

Notes

When you will setup another tool that use git hooks (e.g. husky), it might break the galacrypt git hooks config, you can restore it by running again

yarn galacrypt use <key>

This should not break the git hooks of the other tool (it will append our hooks to theirs)

💡 You can always check manually your git hooks, you can find them by running this:

git config core.hooksPath

If this returns nothing, you git hooks are in .git/hooks

disableImplicitDecrypt option

Imagine you have this config:

{
  "files": [
    {
      "input": ".env",
      "output": ".env.galacrypt"
    },
    {
      "input": ".env.staging",
      "output": ".env.staging.galacrypt"
    }
  ]
}

.env would be the local env variables you need for development, each developer might use a slighly different version of this file

If two devs, Bob and Alice are on the project, and Alice modifies her .env file and pushes an updated .env.galacrypt file

When Bob git pull or git rebase to retrieve the changes Alice have made, galacrypt decrypt will be runned, and he will lose his own version of the .env file

This is expected for external configuration like .env.staging but really anoying for local configuration that might differ from developer to developer

This is where disableImplicitDecrypt can be usefull, see the updated config below:

{
  "files": [
    {
      "input": ".env",
      "output": ".env.galacrypt",
      "disableImplicitDecrypt": true
    },
    {
      "input": ".env.staging",
      "output": ".env.staging.galacrypt"
    }
  ]
}

disableImplicitDecrypt informs galacrypt to never implicitly decrypt this file, meaning when you run galacrypt decrypt (e.g. when you git pull or git rebase to fetch the code of other devs) it will not decrypt .env.galacrypt to the .env

💡 You can always force the decryption by running galacrypt decrypt --all or galacrypt decrypt --only path/to/output/file

💡 When you land on a project, you will want to decrypt all the files, including those anoted with disableImplicitDecrypt, that's why the --all option exists:

yarn galacrypt decrypt --all

mode option

By default ("mode": "file") galacrypt encrypts the whole input file into a single blob. That is the right thing for a binary, but it has a downside on a .env file: changing one variable rewrites the entire blob, so two developers touching the same file always end up with an unresolvable git conflict.

The .env mode encrypts only the values, and leaves the variable names in clear text:

{
  "files": [
    {
      "input": ".env.staging",
      "output": ".env.staging.galacrypt",
      "mode": ".env"
    }
  ]
}

With this .env.staging file:

# staging config
DB_HOST=db.staging.internal
DB_PASSWORD=hunter2

export API_TOKEN="tok en"

yarn galacrypt encrypt now produces a line by line .env.staging.galacrypt:

# generated by Galacrypt, do not edit manually
# galacrypt: v=1 mode=.env eof=lf
#galacrypt:0d2f5c0fa97a1df7f69f23184f8fee32:6167d0c1ba86bad7143a2345832120e2eb910ae5161bedb1fbe8e2a79e8d49c9
DB_HOST=1461c1820260c5af26300fc2fd42b454:0aa330b7ac483f1b89c2a8d7d0c48dbf9758779be868f6a21354dc4b78662004
DB_PASSWORD=97bc23dd017b86464a6c776297a01e10:c8a9ec345c305db9d37b893c3bab0dd5

export API_TOKEN=7a6400017e37ae819674e43b2fbbfb82:28eef2f065f96b3f01aadaaccec1edd2
# end

One line of your .env is one line of the encrypted file, so:

  • changing one variable changes exactly one line of the encrypted file
  • if Alice changes DB_PASSWORD while Bob adds a new variable, git merges both without any conflict
  • when there really is a conflict (both changed the same variable), it is a normal one line conflict that you can resolve in your editor

Comments and blank lines are preserved, but the content of the comments is encrypted, so that a secret you left commented out is not published in clear text. Lines that are not a KEY=value pair are encrypted whole. Values quoted over several lines (a PEM private key for example) are supported and restored as they were.

💡 You can switch an existing file to the .env mode at any time, just add the option and run yarn galacrypt encrypt. Removing the option switches it back the same way. Decryption detects the format of the file itself and not the configured mode, so a teammate who pulls before the file has been re-encrypted is never blocked, in either direction.

⚠️ Before enabling the .env mode on a file, make sure every developer of the project runs galacrypt 2.4.0 or later: an older version cannot read a file encrypted in that format.

What the .env mode reveals

The point of this mode is to trade a bit of confidentiality for mergeable files. Anyone who can read your repository sees:

  • the names of your variables, the export prefix and the indentation
  • how many variables there are, and where the comments and blank lines sit
  • whether a given value changed between two commits (encryption is deterministic, so an unchanged value keeps the exact same encrypted text). The value itself stays secret.

⚠️ If the variable names themselves are sensitive, keep the default file mode.

The .env mode only supports UTF-8 text files. Binaries (and anything you would rather keep fully opaque) must stay in file mode.

About

transcrypt alternative for nodejs ecosystem

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages