This release fixes a critical regression where RTSP streams with sample rates below 48 kHz (typical IP cameras send 8-16 kHz audio) cut in and out after the bat detection changes in nightly-20260523. All streams are now probed at startup so resampling only happens when needed. This release also adds time-windowed health evaluation with sparkline visualizations, SQLite corruption detection with auto-recovery at startup, and completes settings hot-reload coverage for the remaining 5 settings that previously required a restart.
New Features
Time-Windowed Health Evaluation with Sparkline Visualization
Health diagnostics no longer evaluate lifetime counters. A past burst of errors previously marked the system as Critical forever until restart; checks now evaluate configurable time windows (15m to 7d, default 1h). The System Health page is redesigned to match the System Overview visual language: a status metric strip replaces the old hero card, diagnostics auto-run on page mount, and counter-based checks show inline SVG sparklines (24 hourly bars, colored by status) with expandable detail panels. The window selector persists to localStorage. 22 new i18n keys fully translated across all 15 locales (#3249).
Complete Settings Hot-Reload Coverage
Five settings categories that were saved to disk but required a restart to take effect now hot-reload at runtime: Log Deduplication, RTSP Health monitoring, MQTT HomeAssistant discovery, System Monitoring, and HLS LiveStream. This brings the hot-reload TODO count from 5 to 0: every setting changed through the UI now takes effect immediately (#3254).
SQLite Corruption Detection at Startup with Auto-Recovery
A synchronous PRAGMA quick_check now runs at startup after migration. When index-level corruption is detected (common from power loss on SD cards), BirdNET-Go attempts automatic recovery via REINDEX. If recovery fails, the system continues in degraded mode so headless users keep web UI access. A corruption flag is latched to suppress duplicate Sentry event floods (prevents the 244+ duplicate events seen in production), and a persistent notification with actionable recovery guidance is sent to the user. A new DatabaseIntegrityCheck appears in the health diagnostics system (#3246).
Security
- Temp file paths hardened against symlink attacks - Predictable temp file paths across datastore, API prerequisites, and log rotation are replaced with
os.CreateTemp(random name +O_EXCL). Backup files and archives now use0600permissions instead of theos.Createdefault. Database connection pools are properly closed when post-Open()initialization fails (#3248).
Bug Fixes
Audio & Streaming
- RTSP streams with sub-48 kHz sample rates cut in and out - The bat detection changes in nightly-20260523 removed FFmpeg's
-ar/-acoutput resampling flags for all protocol streams, which broke cameras that send 8-16 kHz audio since the pipeline expects 48 kHz. All streams are now probed viaffprobeat startup to discover actual sample rates. Resampling is applied only when the source rate differs from the target: sub-48 kHz sources get resampled up, 48 kHz sources skip resampling, and high-rate bat sources preserve their ultrasonic content. A newSourceSampleRatefield carries the probed rate through the full pipeline. Fixes #3255 (#3256). - Model reload used stale settings, locale changes required restart -
ReloadModel()read from a stalebn.Settingspointer, so changing the locale or thread count via the UI had no effect until a full restart. All sub-methods now see a fresh settings snapshot. Five stale settings reads in HLS streaming are also fixed (#3253). - Duplicate ORT notifications on range filter reload - The v3 geomodel path bypassed the early ONNX Runtime availability check, causing repeated Sentry events and bell notifications on every range filter reload when ORT was unavailable. A single check at the top of model initialization now guards both paths (#3245).
Health Diagnostics
- Health check endpoint could hang indefinitely -
runChecksspawned goroutines with 10s per-check contexts and then calledwg.Wait()unconditionally. If a check ignored its context (e.g.,gopsutilsyscalls), the/api/v2/system/diagnostics/runendpoint blocked forever. A channel-based approach now respects an overall timeout with a 100ms grace period, returningStatusUnknownfor unfinished checks. Multi-result slices are defensively copied to prevent data races (#3251).