fix: audit-driven hardening — cache scope, pool checkout, atomic writes (v2.0.42)
After v2.0.41 shipped, ran a 4-axis audit (file-write races, global locks, ambiguous null returns, missing dashboard UX). Three P0/P1 fixes worth shipping immediately: 1. cache.js cacheKey hashed only the request body. Two callers sending identical "hi" could read each other's cached response (cross-tenant leak). Now scopes by callerKey: hash(callerKey + '\0' + body). chat.js threads callerKey through. 2. conversation-pool.js checkout deleted the entry BEFORE validating callerKey/expected. A fingerprint collision from a different caller would discard the rightful owner's cached cascade. Now validates first; only deletes on actual hit (and on expiry, where deletion is correct). 3. runtime-config / proxy.json / model-access.json / stats.json all used bare writeFileSync(target, JSON.stringify(...)). A SIGTERM mid-write truncated the file; next start logged a warning, fell back to defaults — silent settings loss. Factor src/fs-atomic.js writeJsonAtomic helper (tmp + renameSync, unlink-on-failure) matching the pattern accounts.json already used. Wire into all 4 call sites. 14 new regression tests; 457/457 pass (was 443). Backlog items found by the audit but deferred: dashboard backup/restore, runtime settings UI, first-run wizard, persistent log download, model catalog refresh, per-account discoveredFreeModels, proxyKey+auth, structured no-account reasons. See release notes for full list.
D
dwgx committed
99ccc850dbef1ceb9fb6bffdc92cec59daa01912
Parent: a252654