diff --git a/PROGRESS.md b/PROGRESS.md index 32dc127..94b7709 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -7,7 +7,7 @@ ARCHITECTURE.md for design details. ## Current Status -**Phase:** Phase 2 complete (Steps 9–16). Remote sync fully implemented. +**Phase:** Phase 2 complete. Phase 3 (Encryption) planned, ready for Step 17. **Last updated:** 2026-03-24 @@ -38,20 +38,19 @@ ARCHITECTURE.md for design details. ## In Progress -Phase 2: gRPC Remote Sync. +(none) ## Up Next -Phase 2 is complete. Future work: TLS transport, shell completions, multi-repo server. +Phase 3: Encryption. Step 17 (passphrase-only core) is next. ## Known Issues / Decisions Deferred -- **Blob durability**: blobs are not stored in git. A strategy for backup or - replication is deferred to a future phase. -- **gRPC remote mode**: Phase 2. Package structure is designed to accommodate - it (garden core separates logic from CLI wiring). -- **Clock abstraction**: Done — `jonboulle/clockwork` injected. E2e test - uses fake clock for deterministic timestamps. +- **Manifest signing**: deferred — trust model (which key signs, how do + pulling clients verify) needs design. +- **DEK rotation**: `sgard encrypt rotate-dek` (re-encrypt all blobs) + deferred to future work. +- **FIDO2 testing**: hardware-dependent, may need mocks or CI skip. ## Change Log @@ -77,3 +76,5 @@ Phase 2 is complete. Future work: TLS transport, shell completions, multi-repo s | 2026-03-23 | 14 | SSH key auth: server interceptor (authorized_keys, signature verification), client PerRPCCredentials (ssh-agent/key file). 8 tests including auth integration. | | 2026-03-24 | 15 | CLI wiring: push, pull, prune commands, sgardd daemon binary, --remote/--ssh-key flags, local prune with 2 tests. | | 2026-03-24 | 16 | Polish: updated all docs, flake.nix (sgardd + vendorHash), goreleaser (both binaries), e2e push/pull test with auth. | +| 2026-03-24 | — | JWT token auth implemented (transparent auto-renewal, XDG token cache, ReauthChallenge fast path). | +| 2026-03-24 | — | Phase 3 encryption design: selective per-file encryption, KEK slots (passphrase + fido2/label), manifest-embedded config. | diff --git a/PROJECT_PLAN.md b/PROJECT_PLAN.md index b05b4a5..6f5486b 100644 --- a/PROJECT_PLAN.md +++ b/PROJECT_PLAN.md @@ -177,8 +177,57 @@ Depends on Steps 13, 14. - [x] E2e integration test: init two repos, push from one, pull into other (with auth) - [x] Verify: all tests pass, full push/pull cycle works -## Future Steps (Not Phase 2) +## Phase 3: Encryption + +### Step 17: Encryption Core (Passphrase Only) + +- [ ] `manifest/manifest.go`: add `Encrypted`, `PlaintextHash` fields to Entry; add `Encryption` section with `KekSlots` map to Manifest +- [ ] `garden/encrypt.go`: `EncryptInit(passphrase string) error` — generate DEK, derive KEK via Argon2id, wrap DEK, store in manifest encryption section +- [ ] `garden/encrypt.go`: `UnlockDEK() ([]byte, error)` — read slots, try passphrase, unwrap DEK; cache in memory for command duration +- [ ] `garden/encrypt.go`: encrypt/decrypt helpers using XChaCha20-Poly1305 (nonce + seal/open) +- [ ] `garden/garden.go`: modify Add to accept `--encrypt` flag — encrypt blob before storing, set `encrypted: true` and `plaintext_hash` on entry +- [ ] `garden/garden.go`: modify Checkpoint to re-encrypt changed encrypted entries +- [ ] `garden/restore.go`: modify Restore to decrypt encrypted blobs before writing +- [ ] `garden/diff.go`: modify Diff to decrypt stored blob before diffing +- [ ] `garden/garden.go`: modify Status to use `plaintext_hash` for encrypted entries +- [ ] Tests: round-trip add-encrypted → checkpoint → restore, verify decrypted content matches; status on encrypted entry; diff on encrypted entry +- [ ] Verify: `go test ./... && go vet ./... && golangci-lint run ./...` + +### Step 18: FIDO2 Support + +Depends on Step 17. + +- [ ] `garden/encrypt_fido2.go`: FIDO2 hmac-secret KEK derivation via libfido2 +- [ ] `garden/encrypt.go`: extend UnlockDEK to try fido2/* slots first (check credential_id against connected devices), fall back to passphrase +- [ ] `garden/encrypt.go`: `AddFIDO2Slot(label string) error` — unlock DEK via existing slot, register FIDO2 credential, wrap DEK, add slot to manifest +- [ ] Tests: FIDO2 slot add/unwrap (may need mock or skip on CI without hardware) +- [ ] Verify: `go test ./... && go vet ./... && golangci-lint run ./...` + +### Step 19: Encryption CLI + Slot Management + +Depends on Steps 17, 18. + +- [ ] `cmd/sgard/encrypt.go`: `sgard encrypt init [--fido2]` — creates DEK + passphrase slot (+ FIDO2 slot if --fido2) +- [ ] `cmd/sgard/encrypt.go`: `sgard encrypt add-fido2 [--label]` — adds FIDO2 slot +- [ ] `cmd/sgard/encrypt.go`: `sgard encrypt remove-slot ` — removes a slot (refuse if it's the last one) +- [ ] `cmd/sgard/encrypt.go`: `sgard encrypt list-slots` — print slot names and types +- [ ] `cmd/sgard/encrypt.go`: `sgard encrypt change-passphrase` — re-wrap DEK with new passphrase +- [ ] `cmd/sgard/add.go`: add `--encrypt` flag +- [ ] Update proto: add `encrypted`, `plaintext_hash` to ManifestEntry; add encryption section to Manifest message +- [ ] Update `server/convert.go`: handle new fields in proto conversion +- [ ] Verify: both binaries compile, `go test ./...` + +### Step 20: Encryption Polish + Release + +- [ ] E2e test: add encrypted + plaintext files, push to server, pull to fresh repo, decrypt and verify +- [ ] Update ARCHITECTURE.md, README.md, CLAUDE.md +- [ ] Update flake.nix vendorHash if deps changed +- [ ] Verify: all tests pass, lint clean + +## Future Steps (Not Phase 3) - Shell completion via cobra - TLS transport (optional --tls-cert/--tls-key on sgardd) - Multiple repo support on server +- Manifest signing (requires trust model design) +- DEK rotation (`sgard encrypt rotate-dek` — re-encrypt all blobs)