7dbf34c163
Three independent hardening changes: 1. bcrypt on a blocking thread: hash() and verify() are CPU-bound (~300 ms at cost 12). Running them directly on an async task starved the Tokio runtime under concurrent load. Wrapped in spawn_blocking. 2. Async avatar file I/O: std::fs::write/rename/remove_file in an async handler blocks the executor. Replaced with tokio::fs equivalents. 3. JWT_SECRET minimum length: a secret shorter than 32 bytes is fatally weak. validate_jwt_secret() now rejects it at startup with a clear message rather than silently accepting it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>