# Changelog All notable changes to FastSync are documented here. Versions match `PROTOCOL_VERSION` (printed by `fastsync --version`); the client and server must run the same version because the handshake is strict. ## [2.19.0] - 2026-09-12 ### Security - **Daemon authentication rewritten as SCRAM-SHA-256 challenge/response** (`STATUS_AUTH_CHALLENGE` → `STATUS_AUTH_RESPONSE` → `STATUS_AUTH_OK`/`STATUS_AUTH_FAILED`), replacing the old replayable static `SHA-256(password)` bearer credential. Each proof is bound to a fresh per-connection server nonce plus a client nonce, so a captured response can never be reused. - **Salted verifier store.** `--password-file`/`--early-input` now hold `user:$fastsync$1$pbkdf2-sha256$$$$` (PBKDF2-HMAC-SHA256, default 600000 iterations, range 100000–10000000). The legacy `user:SHA256HEX` form is hard-rejected; there is no auto-upgrade. Generate stores offline with `fastsync-server --hash-credentials FILE [--iterations N]`. - **Username-enumeration hardening.** Unknown/off-list users are answered with a dummy verifier whose salt is a deterministic per-username value (`HMAC-SHA256(dummy_key, username)`), using the store-wide uniform iteration count and a constant-time full-length membership scan. The dummy key is persisted in an owner-only `.dummykey` sidecar (atomic publish, exact mode 0600) so challenges are stable across restarts. - **Verified transport for auth-required modules.** A module with `auth users` accepts credentials only over verified TLS whose client certificate matches `--client-cn`, or — when `--allow-unauthenticated` is explicitly set — plaintext from a loopback peer. Remote plaintext is refused before any challenge. Clients must use `--tls` to send `--password-file` credentials to a non-loopback daemon; `--client-cn` is mandatory with `--tls`. - **Secret hygiene.** The plaintext password, derived keys, nonces/proofs and the dummy key are wiped from memory on every path and never logged. - Carried-over hardening: `-K` TOCTOU-safe directory walk (`openat(O_NOFOLLOW)` per component), always shell-quoted SSH remote path, TLS compression/renegotiation disabled, race-free (open-then-`fstat`) `--password-file`/`--early-input` checks, log-injection escaping, and lazy protocol debug escaping. ### Added - `fastsync-server --hash-credentials FILE [--iterations N]` offline tool. - `.dummykey` sidecar (auto-created, owner-only, 0600). - Integration tests for auth replay rejection, malformed frames, legacy-store refusal, and the loopback/TLS transport policy; fuzz targets for config receive and daemon-auth parsing. ### Changed - **Protocol version 2.18.0 → 2.19.0 (breaking).** The config-frame auth block is now `[present][username]` (digest removed) and the auth challenge/response frames are interleaved between the config frame and its `STATUS_OK`. A 2.19.0 client and a 2.18.0 server (or vice versa) fail cleanly at the handshake. - Daemon modules declaring `auth users` require a configured credential store at startup (fail closed); operators regenerate stores from plaintext with `--hash-credentials`. ### Notes - First tagged release. FastSync implements rsync-compatible file synchronization over TCP and SSH with TLS (OpenSSL), streaming zstd compression, multithreaded transfers, and incremental sync. See [RSYNC_COMPAT.md](RSYNC_COMPAT.md) for the flag-parity matrix.