git-xcrypt 0.1.1

Transparent, deterministic encryption of selected files in a git repository: plaintext in your working tree, ciphertext in the remote.
Documentation
[package]
name = "git-xcrypt"
version = "0.1.1"
edition = "2024"
# Measured, not guessed. Edition 2024 alone needs 1.85, but the code uses `let`
# chains in `if` conditions, which are stable only from 1.88 — on 1.85 the crate
# does not compile at all (12 × E0658). The whole suite passes on 1.88, so that
# is the floor, and the `msrv` job in CI holds it there: a bump has to be a
# decision, not a side effect of an edit nobody noticed.
rust-version = "1.88"
license = "MIT OR Apache-2.0"
description = "Transparent, deterministic encryption of selected files in a git repository: plaintext in your working tree, ciphertext in the remote."
repository = "https://github.com/rkarpin1/git-xcrypt"
homepage = "https://github.com/rkarpin1/git-xcrypt"
documentation = "https://docs.rs/git-xcrypt"
readme = "README.md"
keywords = ["git", "encryption", "secrets", "aes-siv", "cli"]
categories = ["command-line-utilities", "cryptography", "development-tools"]
exclude = ["/context", "/.idea", "/.github"]

[lints.rust]
# AGENTS.md makes "zero unsafe" a hard rule; this is what enforces it.
unsafe_code = "forbid"

[dependencies]
# A pre-release, deliberately, and it buys one thing: `aes-siv` 0.8 pulls `aes`
# 0.9, which compiles the aarch64 backend unconditionally and picks it at
# *runtime* through `cpufeatures` — no opt-in cfg. That is what lets
# `.cargo/config.toml` go away, and with it the gap where `cargo install
# git-xcrypt` from a registry ran the software backend on Apple Silicon while a
# build from a clone did not.
#
# The risk is narrower than "rc" suggests, measured rather than assumed: the
# only pre-release package in the whole graph is this one — `aead` 0.6.1,
# `cipher` 0.5.2, `digest` 0.11.3, `cmac` 0.8.0, `ctr` 0.10.1, `dbl` 0.5.0 and
# `aes` 0.9.2 all resolve to stable, and every one of them declares MSRV 1.85,
# so the 1.88 floor is untouched. The requirement lifts itself to 0.8.0 when
# that lands.
#
# Not one stored byte moves: all four frozen vectors in
# `tests/format_vectors.rs` reproduce byte for byte under 0.8, decrypt back, and
# RFC 5297 A.1 still matches. RFC 5297 is frozen, so the crate version cannot
# enter the format — but that is the argument, and the vectors are the proof.
aes-siv = "0.8.0-rc.3"
base64 = "0.23"
bstr = "1.13.0"
clap = { version = "4.6.5", features = ["derive"] }
getrandom = "0.4.3"
# `status` has to answer "would git run our filter for this path" the way
# `git check-attr` answers it — nested `.gitattributes`, `info/attributes`,
# `[attr]` macros, last match wins. Naming suspicious lines instead was the last
# route to a green report on a repository that stores plaintext. Measured before
# adding it: `cargo add` locks exactly one package, because everything this
# crate needs is already in the graph through `gix-config` and `gix-glob`.
gix-attributes = "0.34.0"
gix-config = "0.59.0"
gix-discover = { version = "0.54.0", features = ["sha1"] }
gix-glob = "0.27.0"
gix-hash = { version = "0.26", features = ["sha1", "sha256"] }
# The history scan needs to read the object database itself: `status` has to
# answer whether a declared path ever sat in a commit in the clear, and no
# amount of working-tree inspection can. Both hash kinds, because `gitindex`
# already handles SHA-256 repositories and a scan that quietly skipped them
# would be the wrong kind of silence.
gix-object = { version = "0.63.0", features = ["sha1", "sha256"] }
gix-odb = { version = "0.83.0", features = ["sha1", "sha256"] }
gix-path = "0.12.4"
gix-ref = { version = "0.66.0", features = ["sha1", "sha256"] }
hkdf = "0.13.0"
sha2 = "0.11.0"
thiserror = "2.0.19"
zeroize = { version = "1.9.0", features = ["derive"] }

[dev-dependencies]
proptest = "1.11.0"
tempfile = "3.27.0"

# A shipped binary is what FR-011 promises, so the release profile is part of
# the product rather than a preference. `panic = "abort"` is deliberately *not*
# set: `lock` and `unlock` walk a working tree file by file, and an abort there
# would skip the reporting that tells a user which files were already rewritten.
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"