Two-layer key hierarchy: DEK (random, encrypts blobs) wrapped by
KEK (derived from passphrase via Argon2id or FIDO2 hmac-secret).
XChaCha20-Poly1305 for both blob encryption and DEK wrapping.
Post-encryption hashing (manifest hash = SHA-256 of ciphertext).
Plaintext hash stored separately for efficient status checks.
Multiple KEK sources per repo. Server never sees the DEK.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two rejection paths: expired-but-valid tokens get a ReauthChallenge
with a server-generated nonce (fast path, saves a round trip).
Invalid/corrupted tokens get plain Unauthenticated (client falls back
to full self-generated auth flow).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two-layer auth: SSH key signing to obtain a 30-day JWT, then
token-based auth for all subsequent requests. Auto-renewal is
transparent — client interceptor catches Unauthenticated, re-signs,
caches new token, retries. Server is stateless (JWT signed with
repo-local secret key). Token cached at $XDG_STATE_HOME/sgard/token.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ARCHITECTURE.md: move mirror/prune to local command table, fix
remove description (prune cleans blobs, not checkpoint), fix
Phase 2 section to only list remote commands
- README.md: add --force to mirror down, fix prune --remote
description, build instructions include both binaries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>