radicle-heartwood-lfs/crates
Maciek "mab122" Bator e060442017 Add client-side encryption for private-repo LFS objects
Radicle's Visibility::Private is enforced entirely by access control at
the replication layer -- git objects for a private repo are plain,
unencrypted objects, protected only by "unauthorized peers are never
given them" (identity/doc.rs). That protection has no jurisdiction over
IPFS's own block-exchange layer: once a legitimate collaborator's IPFS
daemon pins content and joins the network, anyone who obtains the CID by
any means can fetch the plaintext directly.

Adds envelope encryption for private repos: a random per-object content
key encrypts the file (XChaCha20-Poly1305), wrapped once per authorized
recipient (delegates + private allow-list) via ECDH between Radicle
identity keys (already Ed25519, no separate keypair needed) plus HKDF.
The wrapped keys travel in the same refs/notes/rad-lfs note as the CID.

New:
- crates/radicle-cli/src/lfs_crypto.rs -- encryption core (envelope
  schema, encrypt/decrypt, key wrapping, recipient resolution, signer
  loading).
- `rad lfs store`/`rad lfs fetch` -- plumbing commands that now own the
  entire IPFS add/cat + git-notes read/write step (encrypting/decrypting
  as needed), replacing what the pre-commit hook and rad-lfs-transfer
  used to do directly. This collapses what would've become a third copy
  of the note-format logic down to one canonical implementation.
- `rad lfs rekey` -- since Radicle's access control is retroactive (a
  newly-authorized DID can replicate full history) but encrypted objects
  aren't automatically re-wrapped for new recipients, this command lets
  an already-authorized collaborator grant a newly-authorized one access
  to previously-committed objects, without re-encrypting or re-uploading
  content. Revocation remains unsolved, matching Radicle's own model
  (already-replicated/decrypted content can't be un-known).

Operational requirement: private-repo LFS operations need a signer
capable of ECDH, which only an unencrypted keystore or RAD_PASSPHRASE
provides (an ssh-agent-only signer can sign but not do key agreement).
Fails fast with an actionable error rather than hanging on a passphrase
prompt inside a non-interactive git hook.

Breaking change: the refs/notes/rad-lfs note format moves from bare
`cid=<cid>` text to JSON (`{"v":1,"cid":"...","enc":...}`). No migration
path -- prototype, no external users yet.

Verified end-to-end with real Radicle profiles/identities (not just the
transfer-agent protocol): a private repo's IPFS-stored content is
confirmed ciphertext (byte-different from plaintext, +16 bytes AEAD tag);
an authorized peer decrypts correctly; an unauthorized peer is denied
with a clear error and no leaked content; RAD_PASSPHRASE is required and
enforced with a fast, clear failure; and rekey correctly grants a
newly-authorized peer access to a pre-existing object while leaving other
recipients' entries untouched.
2026-07-14 16:01:49 +02:00
..
radicle radicle/identity: Tidy up 2026-07-09 16:26:25 +02:00
radicle-cli Add client-side encryption for private-repo LFS objects 2026-07-14 16:01:49 +02:00
radicle-cli-test cli-test: Disable normalizing paths 2026-06-03 11:49:41 +02:00
radicle-cob cob/change_graph: Log Errors 2026-05-28 16:58:55 +01:00
radicle-core core: Release 0.3.0 2026-05-06 21:00:14 +01:00
radicle-crypto crypto: Handle default `SSH_AUTH_SOCK` on Windows 2026-05-26 09:18:07 +02:00
radicle-dag rust/msrv: 1.85.0 → 1.88.0 2026-05-11 11:23:18 +01:00
radicle-fetch fetch: Update gix packages 2026-05-18 14:57:33 +02:00
radicle-git-metadata rust/msrv: 1.85.0 → 1.88.0 2026-05-11 11:23:18 +01:00
radicle-git-ref-format git-ref-format: New crate 2025-10-13 13:34:15 +02:00
radicle-localtime localtime: add description in Cargo.toml 2026-02-20 13:48:14 +00:00
radicle-log log: New crate for logger implementations 2026-05-11 16:44:37 +01:00
radicle-node node/wire: Test connected node removes active fetch 2026-06-22 18:02:57 +01:00
radicle-oid oid: Release 0.2.0 2026-05-06 21:00:14 +01:00
radicle-protocol fetcher: Test removing active fetch from another node 2026-06-22 18:02:57 +01:00
radicle-remote-helper remote-helper: Introduce magic push ref 'refs/for/' 2026-06-09 11:37:26 +02:00
radicle-schemars schemars: Release 0.8.0 2026-05-06 21:15:54 +01:00
radicle-signals rust/edition/fmt: 2021 → 2024 2026-03-30 16:57:51 +02:00
radicle-systemd systemd: Release 0.13.0 2026-05-06 21:00:14 +01:00
radicle-term Fix NO_COLOR to only suppress colors, not all ANSI styles 2026-06-23 17:23:36 +01:00
radicle-windows rust/edition/fmt: 2021 → 2024 2026-03-30 16:57:51 +02:00