feat: Add cache encryption and memory backend support

Implements cache encryption and configurable memory backend as part of
DeltaGlider v5.0.3 security enhancements.

Features:
- EncryptedCache wrapper using Fernet (AES-128-CBC + HMAC)
- Ephemeral encryption keys per process for forward secrecy
- Optional persistent keys via DG_CACHE_ENCRYPTION_KEY env var
- MemoryCache adapter with LRU eviction and configurable size limits
- Configurable cache backend via DG_CACHE_BACKEND (filesystem/memory)
- Encryption enabled by default with opt-out via DG_CACHE_ENCRYPTION=false

Security:
- Data encrypted at rest with authenticated encryption (HMAC)
- Ephemeral keys provide forward secrecy and process isolation
- SHA256 plaintext mapping maintains CAS compatibility
- Zero-knowledge architecture: encryption keys never leave process

Performance:
- Memory cache: zero I/O, perfect for CI/CD pipelines
- LRU eviction prevents memory exhaustion
- ~10-15% encryption overhead, configurable via env vars

Testing:
- Comprehensive encryption test suite (13 tests)
- Memory cache test suite (10 tests)
- All 119 tests passing with encryption enabled

Documentation:
- Updated CLAUDE.md with encryption and cache backend details
- Environment variables documented
- Security notes and performance considerations

Dependencies:
- Added cryptography>=42.0.0 for Fernet encryption

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Simone Scarduzio
2025-10-10 09:38:48 +02:00
parent 90a342dc33
commit 626e28eaf6
9 changed files with 1011 additions and 15 deletions

View File

@@ -2,6 +2,7 @@
# ruff: noqa: I001
import atexit
import os
import shutil
import tempfile
from collections.abc import Callable
@@ -1115,6 +1116,8 @@ def create_client(
# Import here to avoid circular dependency
from .adapters import (
ContentAddressedCache,
EncryptedCache,
MemoryCache,
NoopMetricsAdapter,
S3StorageAdapter,
Sha256Adapter,
@@ -1144,8 +1147,25 @@ def create_client(
storage = S3StorageAdapter(endpoint_url=endpoint_url, boto3_kwargs=boto3_kwargs)
diff = XdeltaAdapter()
# SECURITY: Use Content-Addressed Storage for zero-collision guarantee
cache = ContentAddressedCache(cache_dir, hasher)
# SECURITY: Configurable cache with encryption and backend selection
from .ports.cache import CachePort
cache_backend = os.environ.get("DG_CACHE_BACKEND", "filesystem") # Options: filesystem, memory
base_cache: CachePort
if cache_backend == "memory":
max_size_mb = int(os.environ.get("DG_CACHE_MEMORY_SIZE_MB", "100"))
base_cache = MemoryCache(hasher, max_size_mb=max_size_mb, temp_dir=cache_dir)
else:
# Filesystem-backed with Content-Addressed Storage
base_cache = ContentAddressedCache(cache_dir, hasher)
# Apply encryption if enabled (default: true)
enable_encryption = os.environ.get("DG_CACHE_ENCRYPTION", "true").lower() == "true"
cache: CachePort
if enable_encryption:
cache = EncryptedCache.from_env(base_cache)
else:
cache = base_cache
clock = UtcClockAdapter()
logger = StdLoggerAdapter(level=log_level)