Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4169,6 +4169,31 @@ systems. This track claims the space.
Composes with the existing lattice-PQC entry below and
with the existing security-operations-engineer persona
scope.
**Candidate set after 2026-04-21 decisions:**
**git-crypt REJECTED 2026-04-21** — Aaron after reading
the cartographer pass: *"git crypto no go i read your
initial review"*. Encoded in `docs/WONT-DO.md`
(*Engineering patterns → git-crypt for secrets
management*). `git-secret` is also out by sibling reasoning
— same OpenPGP-GPG base, same no-revocation property, same
binary-diff problem. **Remaining candidates for the ADR:**
SOPS (KMS / Vault / age backend; plaintext keys +
encrypted values → review-grade diffs; external KMS →
clean rotation) and `age` (modern file encryption; X25519
+ scrypt; draft PQC profile). ADR scope narrows to these
two plus any hybrid posture (one tool for long-lived
secrets, another for ephemeral).
**Research inputs (rationale kept, decision recorded):**
- `docs/research/git-crypt-deep-dive-2026-04-21.md` —
cartographer pass on git-crypt, now marked REJECTED at
the top. Three values-level mismatches: no access
revocation (retraction-mismatch with
`docs/CONFLICT-RESOLUTION.md` Value #4), binary diffs
break code review, metadata leak by design
(`.gitattributes` layout + filenames + commit messages
all in plaintext). Kept per Aaron *"keeep the reserach
... so i don't ask you tomorrow"* — the research IS the
durable "why we said no" artifact.

- [ ] **Adopt at least one NIST-standardised post-quantum
primitive — ADR first, then pick one use case** — Aaron
Expand Down
39 changes: 39 additions & 0 deletions docs/WONT-DO.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,45 @@ Entries are grouped by area. Each entry has:
- **Revisit when:** Either project ships a license-compatible,
production-grade, CPU-runnable variant.

### git-crypt for secrets management

- **Decision:** 2026-04-21
- **Proposal:** Adopt `git-crypt` (GPG / symmetric, AES-256-CTR
with SHA-1-HMAC-derived IV) for secrets-at-rest inside the
repo — the P2 BACKLOG row *"Gitops-friendly key management
+ rotation"* named it as one of four candidates alongside
`git-secret`, SOPS, and `age`.
- **Why not:** Three Zeta-values-level mismatches, documented
in `docs/research/git-crypt-deep-dive-2026-04-21.md`:
1. **No access revocation.** Upstream authors explicit:
once a user has the key, they have every historical
version of every encrypted file, forever. Opposite of
retraction-native (`docs/CONFLICT-RESOLUTION.md` Value
#4).
2. **Binary diffs break code review.** Encrypted files
appear as opaque blobs — reviewers cannot tell a key
rotation from a key theft. Breaks the review gate the
rest of the factory depends on.
3. **Metadata leak by design.** Filenames, commit
messages, symlink targets, and `.gitattributes` layout
are all in plaintext. A pattern like
`secrets/prod.yaml filter=git-crypt` tells the world a
`prod.yaml` secret exists; only the contents are hidden.
Additional (documented, non-decisive) concerns: pre-v1.0
with authors reserving compat-break rights, third-party
git-GUI silent-plaintext leaks, non-compressible storage,
and no HSM/PQC story. The three values-level mismatches
alone are sufficient.
- **Revisit when:** Upstream adds in-place revocation without
history-rewrite, *and* renders encrypted content in
review-friendly diffs, *and* ships a filename-protection
story. Realistically: never — these are architectural
constraints of the design, not missing features. SOPS +
KMS (for long-lived secrets) and `age` (for ephemeral /
PQC-curious surfaces) remain the candidate set for the
eventual ADR; `git-secret` also ruled out by sibling
reasoning. Research artifact kept as the rationale.

---

## Repo / process
Expand Down
256 changes: 256 additions & 0 deletions docs/research/git-crypt-deep-dive-2026-04-21.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
# git-crypt deep-dive — 2026-04-21

> **Decision (2026-04-21): REJECTED.** Aaron, after reading
> this research: *"git crypto no go i read your initial
> review"*. git-crypt is out of the candidate set for Zeta
> secrets-at-rest. Rejection is encoded in
> `docs/WONT-DO.md` (*Engineering patterns → git-crypt for
> secrets management*). This document is **kept as the
> rationale artifact** — *"keeep the reserach ... so i
> don't ask you tomorrow"* — so future-self sees the
> reasoning without re-running it. The remaining candidate
> set for the eventual ADR is SOPS + KMS (long-lived
> secrets) and `age` (ephemeral / PQC-curious); `git-secret`
> is also out by sibling reasoning (same OpenPGP-GPG + no-
> revocation properties).

**Date:** 2026-04-21
**Author:** Architect (Kenji) — cartographer pass, not a
decision. Feeds into the P2 BACKLOG row *"Gitops-friendly key
management + rotation — ADR first, then pick one tool"*
(`docs/BACKLOG.md`).
**Triggering ask:** Aaron, 2026-04-21 — *"while you are here
reserch gitcrypt for secrets on backlog"*.
Comment on lines +18 to +23
**Status:** REJECTED 2026-04-21 (see banner above). Rationale
artifact retained. SOPS / age remain in the candidate set;
ADR under `docs/DECISIONS/YYYY-MM-DD-gitops-key-management.md`
not yet drafted.

## TL;DR

- **git-crypt v0.8.0** (released 2025-09-24, GPL-3.0,
`github.com/AGWA/git-crypt`). 101 open issues, 205
commits on master, still pre-v1.0 with authors reserving
the right to break compatibility.
- **One thing it is great at:** mixed-public-private repos
where a handful of files carry secrets and you want `git
add / commit / push` to keep working transparently via a
`.gitattributes` filter.
- **Three things it is fundamentally bad at — each one a
Zeta-values mismatch:**
1. **No access revocation.** Authors explicit: *"git-crypt
does not support revoking access to an encrypted
repository which was previously granted."* Once a user
has the key, they have every historical version of every
encrypted file, forever. This is the exact opposite of
retraction-native (value #4 in `CONFLICT-RESOLUTION.md`).
2. **Binary diffs break code review.** Encrypted files
appear as opaque blobs in diffs — reviewers cannot read
what changed, which breaks the review gate the rest of
the factory depends on.
3. **Metadata leak by design.** No encryption of filenames,
commit messages, symlink targets, or repo metadata. A
`.gitattributes` entry like `secrets/prod.yaml filter=
git-crypt diff=git-crypt` tells the world there is a
`prod.yaml` secret — only the contents are hidden.
- **Recommendation scaffolding for the ADR:** git-crypt
scores well on gitops-first (value-aligned), poorly on
retraction (value-misaligned), medium on team-scale
(works for ≤5-10 contributors, degrades past that), medium
on audit (commits visible, contents opaque). SOPS with
external KMS scores better on retraction and audit;
`age` scores better on post-quantum readiness and
passphrase UX. The four-way scorecard remains open.

## 1. Current maintenance snapshot

| Metric | Value |
|---|---|
| Version | 0.8.0 |
| Released | 2025-09-24 |
| License | GPL-3.0 |
| Repo | `github.com/AGWA/git-crypt` |
| Open issues | 101 |
| Total commits (master) | 205 |
| Pre-v1.0 | yes; authors may break compat |
| Last security audit | none located (2026-04-21) |

Authors' self-description: *"bug-free and reliable"* but
*"not as documented, featureful, or easy-to-use as it
should be"*. Interpret as: stable primitive, small surface,
maintained but not thriving.

## 2. Security model

Encryption: AES-256 in CTR mode with a synthetic IV derived
from the SHA-1 HMAC of the plaintext (convergent-encryption
style). Provides provable semantic security under
deterministic chosen-plaintext attack that *leaks no
information beyond whether two files are identical*.

Two key modes:

- **Symmetric (shared key)** — one AES key committed into
the repo encrypted under a passphrase. Simple; every user
who can decrypt the passphrase has every secret.
- **GPG (public-key)** — each collaborator's GPG key is
added via `git-crypt add-gpg-user`; the symmetric key is
re-encrypted to every authorised GPG key. Scales better
for small teams; still cannot revoke prior access.

## 3. Five concrete gotchas (collected 2026-04-21)

1. **Revocation is impossible without history rewrite.**
Removing a user requires generating a new symmetric key
AND *rewriting every historical commit* that contains
encrypted content, then force-pushing. This is
prohibitively expensive on an active repo and breaks
every outstanding clone / fork / PR.
2. **Third-party git GUIs can leak plaintext.** Upstream
README warns that *"git-crypt does not work reliably
with some third-party git GUIs, such as Atlassian
SourceTree and GitHub for Mac. Files might be left in an
unencrypted state."* Silent data-plane failure — CI
workflows that blindly trust the local checkout could
commit unencrypted secrets.
3. **`.gitattributes` footguns.** Pattern-matching mistakes
(e.g. `secrets/*` missing sub-directories, or `*.yaml`
over-matching) silently leave files unencrypted. No
tooling to warn; the only signal is staring at
`git diff`.
4. **Non-compressible storage.** Encrypted files are stored
as full versions at every commit (git's delta compression
requires readable content); repo size grows linearly with
(file size × change count). Not a problem at the
secret-file scale typical of this use case, but bounds
how much you can protect this way.
5. **Cannot `cat secret` without unlock state.** All team
automation that touches a secret requires the repo to be
unlocked (`git-crypt unlock`). Forgotten-unlock
incidents are a recurring ops hazard in the community
threads.

## 4. Zeta-specific scorecard (for the ADR)

Axes per the BACKLOG row. Scoring: **good** / *mixed* /
**bad**. Scores are draft; the ADR finalises.

| Axis | git-crypt | Why |
|---|---|---|
| Gitops-first | **good** | Pure-git primitive, no external runtime, every op visible in history. |
| Retraction-friendly | **bad** | Authors explicit: no revocation. Core Zeta-values mismatch. |
| Merge-conflict behaviour | *mixed* | Encrypted files can conflict; resolution requires unlock + manual edit. Fine at ≤5-10 contributors, friction grows. |
| Code review visibility | **bad** | Binary diffs. Reviewer cannot tell a key rotation from a key theft. |
| Metadata protection | **bad** | Filenames + commit messages + `.gitattributes` layout all leak. |
| Team-scale ceiling | *mixed* | Works for small teams; "past 5-10 people and the limitations become problematic" per community consensus. |
| PQC readiness | **bad** | AES + GPG (OpenPGP). Post-quantum migration requires OpenPGP PQC WG finalisation (years). |
| Rotation of signing keys | N/A | Out of scope — signing keys ≠ secrets at rest. |
| HSM integration | **bad** | No HSM story; keys live on disk. |
| Implementation effort | **good** | Hours, not days. `brew install git-crypt` + `.gitattributes` + one-time init. |

## 5. What git-crypt is still good for

- **Single-contributor repos** where revocation is moot
because there's nobody to revoke.
- **Short-lived ephemeral keys** that rotate by
repo-scrap-and-rebuild rather than by in-place revocation
— e.g. a test-only NuGet API key in a throwaway dev
profile (which is exactly the pilot use case the BACKLOG
row names).
- **Config files where leakage of existence is acceptable**
but content must be hidden (e.g. an obviously-named
`deploy-secrets.yaml` whose existence is public, whose
content is not).

The pilot the BACKLOG row proposes (test-only NuGet API
key in a throwaway dev profile) fits the first and second
conditions. If the ADR concludes "git-crypt is fine for the
pilot", that does **not** generalise to the production
posture — which should be re-evaluated against SOPS + KMS
and `age` before broader rollout.

## 6. What git-crypt is explicitly wrong for (Zeta alignment)

- **Any secret whose leak needs to be retractable.** Value
#4 in `CONFLICT-RESOLUTION.md` is retraction-native; a
primitive that cannot retract access is a values-level
mismatch.
- **Any secret whose rotation cadence is shorter than the
repo's life.** Every rotation is an O(history) force-push
incident.
- **Any secret that needs PQC posture before 2028-ish.**
GPG OpenPGP PQC work is slower than age's draft or
SOPS-on-KMS where the KMS already supports PQC hybrids
in preview.
- **Any secret whose leak must survive a compromised
contributor laptop.** Disk-resident GPG private keys
make contributor-laptop compromise equivalent to full
secret disclosure.

## 7. Comparison to the other candidates on the BACKLOG row

Scoring placeholder — expand in the ADR. All three
alternatives score **good** on retraction-friendliness where
git-crypt scores **bad**.

| Candidate | Retraction | Code review | PQC path | Team-scale | GitOps |
|---|---|---|---|---|---|
| git-crypt | **bad** | **bad** (binary diff) | bad | mixed | good |
| git-secret (GPG wrapper) | **bad** | bad | bad | mixed | good |
| SOPS (KMS / Vault / age) | good | **good** (plaintext keys / encrypted values; YAML diffs readable) | good (KMS-dependent; age hybrid draft) | good | good |
| age (modern file encryption) | good (by rotation of recipient identities) | mixed (binary-like, but format simpler, tooling improving) | **good** (draft X25519-PQ) | good (small-team) / mixed (cloud-KMS story thinner than SOPS) | good |

## 8. Cross-references

- `docs/BACKLOG.md` — P2 *Gitops-friendly key management +
rotation* (lines ~4133-4171).
- `docs/BACKLOG.md` — P2 *Adopt at least one
NIST-standardised post-quantum primitive* (sibling; affects
which candidates remain viable long-term).
- `docs/research/ci-retractability-inventory.md` — names
SLSA signing keys as the one genuinely-non-retractable
surface; separate decision from secrets-at-rest.
- `docs/security/CRYPTO.md` — crypto primitive inventory,
hash-based signatures mention.
- `memory/user_security_credentials.md` — nation-state
threat-model anchor.
Comment on lines +213 to +216

## 9. Review panel (per the BACKLOG row, for the ADR)

- **Mateo** (security-researcher) — primary on primitive
selection + PQC path.
- **Nazar** (security-operations-engineer) — primary on
rotation cadence + HSM integration.
- **Aminata** (threat-model-critic) — gate on the
nation-state adversary model.
- **Architect (Kenji)** — integrates + signs the ADR.
Comment on lines +220 to +226

## 10. Open questions for the ADR

1. Is the pilot use case (test-only NuGet API key in a
throwaway dev profile) durable enough that we would
accept git-crypt's no-revocation property, or does even
the pilot deserve a retraction-friendly primitive from
day one?
2. Is the **code-review opacity** of git-crypt a showstopper
on its own? SOPS's plaintext-keys-encrypted-values format
preserves review-grade diffs; that alone may be decisive
regardless of the other axes.
3. Do we need a **hybrid posture**: one primitive for
long-lived secrets (SOPS + KMS for rotation) and a
second for ephemeral-dev-convenience (git-crypt for
solo-dev laptop-only artefacts)? The ADR should state
explicitly whether "one tool wins" or "tools split by
use case".

## Sources

- [git-crypt README — github.com/AGWA/git-crypt](https://github.com/AGWA/git-crypt)
- [Opensource.com — 4 secrets management tools for Git encryption](https://opensource.com/article/19/2/secrets-management-tools-git)
- [Medium / Brian Davis — Securely storing secrets in Git](https://medium.com/@slimm609/securely-storing-secrets-in-git-542771d3ed8c)
- [NixOS Discourse — git-crypt / agenix / sops-nix](https://discourse.nixos.org/t/handling-secrets-in-nixos-an-overview-git-crypt-agenix-sops-nix-and-when-to-use-them/35462)
- [KX Cloudingenium — Mozilla SOPS for GitOps](https://kx.cloudingenium.com/en/sops-secrets-encryption-version-control-gitops-guide/)
- [GitGuardian — Comprehensive guide to SOPS](https://blog.gitguardian.com/a-comprehensive-guide-to-sops/)
- [Infisical — Open-source secrets management 2026](https://infisical.com/blog/open-source-secrets-management-devops)
- [GitGuardian — Secret scanning tools 2026](https://blog.gitguardian.com/secret-scanning-tools/)
- [The Dave Stack — Secrets in Git](https://en.thedavestack.com/git-crypt/)
Loading