A Python CLI tool providing file encryption with .gitattributes-style pattern matching and both symmetric and GPG-encrypted keyfile modes.
Effortless file encryption for your git repos—pattern-matched, secure, and keyfile-flexible.
🔒 Secure by Design: Built with modern cryptographic standards and comprehensive security scanning
- Features
- Quick Start
- Installation
- Usage
- Configuration
- Examples
- Architecture
- Security
- Contributing
- License
# Install from PyPI
pip install gitsafe-cli
# Or install from source
git clone https://github.com/hemonserrat/git-safe.git
cd git-safe
pip install -e .
# Initialize encryption for your repository (automatically sets up .gitattributes and Git filters)
git-safe init
# That's it! Files matching *.secret are now automatically encrypted/decrypted transparently
echo "my secret" > config.secret
git add config.secret # Automatically encrypted when committed
git commit -m "Add secret config"
# File remains readable in your working directory, encrypted in Git history- Transparent Operation: Files are automatically encrypted/decrypted through Git filters - no manual intervention needed
- Pattern Matching: Uses
.gitattributes-style patterns for selecting files to encrypt - Dual Encryption Modes:
- Symmetric encryption (AES-256 CTR + HMAC-SHA256)
- GPG-encrypted keyfile export/import (like git-crypt's
export-key)
- Modular Architecture: Clean separation of concerns with dedicated modules
- Enhanced CLI: Multiple commands for different operations
- File Integrity: HMAC verification for encrypted files
- Backup Support: Optional backup creation during manual encryption
pip install gitsafe-cligit clone https://github.com/hemonserrat/git-safe.git
cd git-safe
pip install -e .git clone https://github.com/hemonserrat/git-safe.git
cd git-safe
pip install -e ".[dev,security]"- Python 3.8 or higher
- GPG (for keyfile sharing functionality)
- Ubuntu/Debian:
sudo apt-get install gnupg - macOS:
brew install gnupg - Windows: Install Gpg4win
- Ubuntu/Debian:
git-safe init [--keyfile PATH] [--force] [--export-gpg RECIPIENT]The init command performs the following setup automatically:
- Creates keyfile: By default at
.git-safe/.git-safe-key(creates.git-safedirectory if needed) - Sets up .gitattributes: Adds
*.secret filter=git-safe diff=git-safeif not already present - Configures Git filters: Sets up local Git configuration for automatic encryption/decryption:
filter.git-safe.clean- Encrypts files on commitfilter.git-safe.smudge- Decrypts files on checkoutfilter.git-safe.required- Makes the filter requireddiff.git-safe.textconv- Enables readable diffs
Note: Git filter setup is skipped if not in a Git repository, with a warning message.
🔐 CRITICAL SECURITY WARNING
The
.git-safedirectory contains your encryption keys and MUST be kept secure:
- Add
.git-safe/to your.gitignore- Never commit keyfiles to your repository- Backup your keyfiles securely - If lost, encrypted files cannot be recovered
- Use GPG export for team sharing - Share keys securely with
git-safe export-keyWithout the keyfile, your encrypted data is permanently inaccessible!
After initialization, git-safe works transparently through Git filters:
# Files matching *.secret are automatically encrypted/decrypted
echo "database_password=secret123" > config.secret
git add config.secret # Automatically encrypted when staged
git commit -m "Add config" # Stored encrypted in Git history
cat config.secret # Still readable in working directory
# No manual encrypt/decrypt needed - it's all automatic!git-safe export-key RECIPIENT [--keyfile PATH] [--output PATH]git-safe unlock [--gpg-keyfile PATH] [--output PATH]- By default, the unlock command will look for
.git-safe/.git-safe-key.gpgif--gpg-keyfileis not specified.
The following commands are optional - Git filters handle encryption/decryption automatically:
git-safe encrypt [--keyfile PATH] [--no-backup] [--continue-on-error]git-safe decrypt [--keyfile PATH] [--all] [--continue-on-error] [PATTERNS...]- Decrypted files will always overwrite the original encrypted files in-place, preserving their names and extensions.
git-safe status [--keyfile PATH]git-safe cleanThe git-safe init command automatically adds the default pattern *.secret filter=git-safe diff=git-safe to your .gitattributes file.
You can add additional patterns to specify which files should be encrypted:
secrets.txt filter=git-safe
*.key filter=git-safe
config/*.secret filter=git-safe
Note: The diff=git-safe attribute enables readable diffs for encrypted files when the Git filter is properly configured.
The tool is organized into several modules:
constants.py: Magic headers and cryptographic constantscrypto.py: Low-level cryptographic operations (AES-CTR, HMAC)keyfile.py: Keyfile generation, loading, and GPG export/importpatterns.py: .gitattributes parsing and pattern matchingfile_ops.py: File encryption, decryption, and managementcli.py: Command-line interface and argument parsing
Encrypted files use the format:
MAGIC (9 bytes) + NONCE (12 bytes) + ENCRYPTED_DATA
Keyfiles use the format:
KEYFILE_MAGIC (12 bytes) + KEY_BLOBS
Where each key blob is:
ID (4 bytes) + LENGTH (4 bytes) + DATA (LENGTH bytes)
- Uses AES-256 in CTR mode for encryption
- HMAC-SHA256 for integrity verification
- Secure random nonce generation
- GPG integration for keyfile sharing
- Restrictive file permissions (0600) for keyfiles
⚠️ WARNING: Keyfile loss means permanent data loss!
Essential Security Practices:
-
Never commit keyfiles to Git:
echo ".git-safe/" >> .gitignore git add .gitignore git commit -m "Ignore git-safe keyfiles"
-
Backup keyfiles securely:
- Store copies in secure, encrypted locations
- Use multiple backup locations (local + cloud)
- Test backup restoration regularly
-
Team collaboration:
# Export for team members git-safe export-key teammate@company.com # Team member imports git-safe unlock --gpg-keyfile shared-key.gpg
-
Key rotation:
- Regularly rotate keyfiles for long-term projects
- Use
--forceflag to overwrite existing keyfiles - Re-encrypt all files after key rotation
Recovery Scenarios:
- ✅ Keyfile backed up: Restore from backup, continue working
- ❌ Keyfile lost, no backup: Encrypted files are permanently unrecoverable
- ✅ Team member has keyfile: Export/import via GPG
This tool is designed to be compatible with git-crypt's file format and workflow, while providing additional features and a more Pythonic implementation.
The original git-safe.py script is maintained for backward compatibility and now uses the new modular architecture internally.
- Complete setup with transparent encryption:
# Initialize keyfile (automatically sets up .gitattributes and Git filters)
git-safe init
# Add .git-safe to .gitignore (IMPORTANT!)
echo ".git-safe/" >> .gitignore
# Create secret files - they work transparently
echo "database_password=secret123" > config.secret
echo "api_key=abc123xyz" > api.secret
# Normal Git workflow - encryption happens automatically
git add .
git commit -m "Add configuration files"
# Files are encrypted in Git, readable in working directory
git show HEAD:config.secret # Shows encrypted binary data
cat config.secret # Shows readable plaintext- Share keyfile with team member:
# Export keyfile for GPG recipient
git-safe export-key alice@example.com
# Team member imports the keyfile
git-safe unlock
# By default, this will look for .git-safe/.git-safe-key.gpg- Manual operations (if needed):
# Manually encrypt files (usually not needed)
git-safe encrypt
# Manually decrypt files (usually not needed)
git-safe decrypt --all
# Check status of encrypted files
git-safe statusWe welcome contributions! Please see our Contributing Guidelines for details.
git clone https://github.com/hemonserrat/git-safe.git
cd git-safe
pip install -e ".[dev,security]"
pre-commit install # Optional: for automated code formatting# Run all tests
pytest
# Run with coverage
pytest --cov=git_safe --cov-report=html
# Run security tests
bandit -r git_safe/
safety checkGPG not found: Ensure GPG is installed and available in your PATH.
gpg --version # Should show GPG versionPermission denied: Keyfiles are created with restrictive permissions (0600). Ensure you have proper file system permissions.
Import errors: If you encounter import errors, try reinstalling:
pip uninstall gitsafe-cli
pip install gitsafe-cli"No keyfile found" error:
# Check if keyfile exists
ls -la .git-safe/.git-safe-key
# If missing, restore from backup or re-initialize
git-safe init --forceGit filter errors:
# Check Git filter configuration
git config --local --list | grep git-safe
# Re-run init to fix configuration
git-safe init --forceFiles not encrypting automatically:
# Check .gitattributes
cat .gitattributes
# Verify file matches pattern
echo "test.secret" | git check-attr --all --stdinEncrypted files show as binary in diffs:
# Check diff filter configuration
git config --local diff.git-safe.textconv
# Should show: git-safe diffSee CHANGELOG.md for a detailed history of changes.
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by git-crypt by Andrew Ayer
- Built with modern Python cryptographic libraries
- Thanks to all contributors who have helped improve this project
- cryptography - Modern cryptographic operations
- python-gnupg - GPG integration
- pathspec - .gitattributes pattern matching