Thanks for your interest in contributing to gitveil! This document covers what you need to know to get started.
- Rust (stable, 1.70+): install via rustup
- Git (2.20+)
- git-crypt (optional, needed for cross-compatibility tests)
- GPG (optional, only needed for GPG-related features)
git clone https://github.com/lucatescari/gitveil.git
cd gitveil
cargo buildcargo testAll 112 tests should pass (34 unit + 56 integration + 16 GPG integration + 6 cross-compatibility). They cover:
- AES-256-CTR encryption/decryption round-trips
- HMAC-SHA1 known-answer vectors
- Key file TLV serialization/deserialization
- Clean/smudge/diff filter round-trips
- Non-encrypted passthrough behavior
- Key name validation
- Full E2E: init → encrypt → lock → unlock (integration)
- Status, export-key, quiet mode, error messages (integration)
- Status: default focused output (tracked + untracked filter-marked only),
(untracked)suffix on untracked filter files to distinguish prospective vs actual encryption,-a/--allincludes non-filter files,-eonly files with encrypted blob,-uonly WARNING files needing re-encryption, WARNING + summary for filter-marked files with plaintext blob, named-key filter, filenames with spaces, clear error outside a git repo, works withoutgitveil init,-fskips files deleted from the working tree, gitignored files are excluded (integration) - Status:
has_git_crypt_filterrecognizes default and named-key filters (unit) - Edge cases: empty files, binary files, multi-key lock (integration)
- Pipe deadlock regression: many-file and large-blob status, unlock, lock (integration)
- Global config: XDG resolution, keyring path save/load/remove, permissions (unit)
- Config CLI: set-keyring, unset-keyring, show, overwrite, canonicalization, symlinks (integration)
- Keyring fallback: add-gpg-user with no args, empty dir, deleted dir, precedence (integration)
- Scan security: symlink skipping, non-key extensions, empty directory (integration)
- GPG add-gpg-user: by email, fingerprint, --trusted, --no-commit, -k, --from file (GPG integration)
- GPG rm-gpg-user: remove, --no-commit, user not found (GPG integration)
- GPG ls-gpg-users: list, no users, named key (GPG integration)
- GPG unlock roundtrip: add user, lock, unlock via GPG (GPG integration)
- GPG unlock with passphrase-protected key (pinentry/loopback) (GPG integration)
- GPG unlock clear error when no secret key matches any collaborator (GPG integration)
- GPG decrypt command builder: never passes
--batch(suppresses pinentry) (unit) - GPG multi-user: add 2 users, remove 1, verify count (GPG integration)
- Cross-tool: key exchange, encrypt/decrypt, named keys, binary files (cross-compatibility)
The cross-compatibility tests (tests/cross_compat.rs) verify interoperability with
git-crypt. They skip automatically when git-crypt is not installed.
cargo run -- <command> [args]
# Examples:
cargo run -- --help
cargo run -- init
cargo run -- statussrc/
crypto/ Core cryptography (AES-CTR, HMAC-SHA1, random)
key/ Key file format (TLV serialization, entries, key container)
filter/ Git clean/smudge/diff filters
commands/ User-facing commands (init, lock, unlock, status, export-key,
add/rm/ls-gpg-users, config)
git/ Git repository helpers (config, checkout, repo inspection)
gpg/ GPG integration (key import, encrypt/decrypt via gpg CLI)
cli.rs clap CLI definitions + shell completion generation
config.rs Global configuration (XDG keyring path)
constants.rs Shared constants (magic bytes, sizes, field IDs)
error.rs Error types
main.rs Entry point
tests/
integration.rs E2E tests using temporary git repos
gpg_integration.rs GPG user management tests (add/rm/ls, unlock via GPG)
cross_compat.rs Cross-tool tests against git-crypt
benchmark/
bench.sh Status command scaling by file count
bench_large_files.sh Status with large binary files (Unity-like repos)
bench_operations.sh Multi-operation comparison (init, status, lock, unlock)
scripts/
release.sh Automated release + Homebrew formula update
.github/
workflows/ci.yml GitHub Actions CI (fmt, clippy, test)
- Run
cargo fmtbefore committing - Run
cargo clippyand fix any warnings - Follow standard Rust naming conventions
This is the most important constraint. Gitveil must remain byte-compatible with git-crypt:
- The key file format must match exactly (header magic, FORMAT_VERSION 2, TLV field IDs and sizes)
- The encrypted file format must match exactly (
\0GITCRYPT\0header, 12-byte HMAC-SHA1 nonce, AES-256-CTR ciphertext) - Git filter names must be
git-crypt/git-crypt-<keyname>(notgitveil)
If you change anything in crypto/, key/, or filter/, verify compatibility against a real git-crypt installation. A file encrypted by gitveil must decrypt correctly with git-crypt, and vice versa.
- All modules use
GitVeilErrorfromerror.rswiththiserrorderive - User-facing error messages should be clear and actionable
- Errors are printed in red via
coloredinmain.rs
- Key material (
aes_key,hmac_key) must be zeroized on drop. TheKeyEntrystruct derivesZeroizeOnDrop. - Never log or print key material, even in debug builds
- Use
rand::rngs::OsRngfor all random generation (not thread-local or seeded RNGs)
- Create
src/commands/your_command.rs - Add it to
src/commands/mod.rs - Add the CLI variant to
src/cli.rsin theCommandsenum - Wire it up in
src/main.rs's match block
- Unit tests go in
#[cfg(test)] mod testsblocks within the relevant source file - Integration tests that need a real git repo should go in a
tests/directory - For crypto tests, use known-answer vectors where possible
- Fork the repository
- Create a feature branch (
git checkout -b my-feature) - Make your changes
- Run
cargo fmt && cargo clippy && cargo test - Commit with a clear message
- Open a pull request
Open an issue if something is unclear or you need guidance on an approach.