This nightly is a large architectural release. Highlights include multi-source audio capture with dynamic device reconfiguration, a new multi-model detection pipeline with Perch v2 and ONNX backend support, a complete rebuild of the audio subsystem, extensive database reliability work, and over 50 fixes across detection, streaming, notifications, and dashboard.
New Features
Multi-Source Audio Capture
BirdNET-Go can now listen to multiple sound cards simultaneously. Each source has its own gain setting and EQ filter configuration. The capture pipeline supports dynamic device reconfiguration — sources can be added, removed, or retargeted from the UI without restarting the server. This is built on the new internal/audiocore package that replaced the monolithic myaudio with a clean, dependency-injected audio architecture (#2612, #2614, #2616, #2737, #2738, #2739).
Multi-Model Detection Groundwork (Preview — BirdNET v2.4 only in this release)
The audio capture pipeline has been rewritten to feed one or multiple AI models in parallel, and a new ONNX inference backend (gated by a build tag) has been added alongside the existing TensorFlow Lite backend. Docker images now ship with ONNX Runtime 1.24.4, and the classifier package has been reorganized into internal/classifier with per-backend sub-packages and a configuration-driven model identity registry.
This is preview plumbing, not a shipping feature yet. This release continues to run BirdNET v2.4 exclusively. Support for Google Perch v2 and the upcoming BirdNET v3.0 is technically wired up but remains untested, no additional models are bundled with the release, and there is no UI for enabling or configuring alternate models. A model catalog UI for easy deployment of additional models — and eventually a bat detection model from the BattyBirdNET-Pi project — is planned for a future release. Expect rough edges as the new pipeline matures. See discussion #2725 for background (#2617, #2619, #2622, #2623, #2624, #2625, #2626, #2628, #2631, #2637, #2639, #2676).
Species Custom Configuration Editor Redesign
The species custom configuration editor has been rebuilt with reusable editor components for a cleaner and more consistent UX, including bidirectional species name resolution between common and scientific names (#2685, #2687).
HTTPS Healthcheck
The container healthcheck now supports HTTPS endpoints, with response validation to correctly handle redirects (#2673 by @mmomjian, #2680).
Consistent UI Transitions
Dropdowns and modals across the UI now share a unified transition style for a more coherent feel (#2563).
Hungarian Locale
New Hungarian (hu) locale added to the UI (#2488 by @cinadr).
GitHub Issue Number in Support Dumps
Support dumps uploaded from Settings > Support can now carry a GitHub issue number that reaches Sentry, making it easier to correlate diagnostic data with reported issues (#2548).
Security
- Redact secrets from settings API response —
GET /api/v2/settingsno longer leaks API keys, webhook credentials, or OAuth secrets (#2527) - Webhook secret restoration by provider + endpoint — replaces positional index matching to prevent secret cross-contamination when providers are reordered (#2531)
- CSRF skipper hardened — restricted to safe HTTP methods on media paths, with path traversal protection (#2536)
- IsSecureRequest trust boundary —
X-Forwarded-Protonow only trusted from loopback/private-network IPs (#2536) - SSE payload sanitization — explicit struct with camelCase fields, strips filesystem paths and source credentials from live event stream (#2531)
- Tighter input validation — stricter CIDR parsing, GPS-in-device, and custom command path validation (#2729)
- X-XSS-Protection disabled — set to
0per OWASP guidance (deprecated browser feature) (#2531)
Bug Fixes
Detection Pipeline & Species Filtering
- "Ignore Species" setting now actually filters — species filter is applied during detection processing with case-insensitive matching on common and scientific names (#2542)
- Species novelty flags —
isNewSpecies,isNewThisYear, andisNewThisSeasonnow compare against first-seen dates instead of always returning true (#2535) - Extended capture settings now hot-reload — species filter map rebuilds without restart (#2542)
- Dynamic threshold goroutines managed on hot-reload — persistence and cleanup goroutines start/stop when the setting toggles (#2542)
- Dynamic threshold recalculation — all species thresholds recalculate when the base threshold changes (#2536)
- Cannot delete species from dynamic threshold list — fixed re-insertion race after batch persistence (#2542)
- Orphaned clip references — disk retention now clears
clip_namereferences when files are deleted (#2660) - DST-safe calendar day calculation in species tracking (#2689)
- Requeue failed pending resets — no longer silently dropped (#2585)
Database Reliability
- Reduce SQLite write contention — addresses silent data loss under concurrent writes (#2565)
- RetryOnLock on all write paths — v1 datastore and v2 repository write paths retry on transient lock errors with exponential backoff and cancellation support (#2577, #2578, #2582)
- MySQL connection pool sizing — aligned v1 and v2 pool constants, prevents resource exhaustion (#2573, #2575)
- Cancelable WAL checkpoint — no longer blocks past shutdown deadline (#2587)
- Periodic SQLite WAL checkpointing — 5-minute PASSIVE checkpoints prevent unbounded WAL growth (#2536)
- v2 database migration robustness — schema validation and self-healing for partially applied migrations (#2683)
- Alerting history cleanup retry — exponential backoff on transient SQLite lock errors (#2536)
- Database backup nil guard — prevents panic in VACUUM INTO when gormDB is nil (#2535)
Audio, Streaming & Spectrograms
- Separate spectrogram gain from audio output mute — the gain slider no longer shares state with the output mute control (#2554)
- Spectrogram style on FFmpeg fallback — consistent appearance whether sox or FFmpeg generates the spectrogram (#2555)
- Dark spectrograms from sox — fixed duration query failures on MP3/AAC files (#2684)
- Decouple audio export from detection broadcast — export no longer blocks the broadcast pipeline (#2567)
- HLS startup gap reduced — larger playlist size plus
independent_segmentsandflush_packetsflags (#2542) - Non-S16 audio format conversion — non-16-bit inputs properly converted to 16-bit PCM (#2612)
- FFmpeg stderr race and audio 404 headers (#2654)
- Skip gzip for binary media and ranged responses — fixes audio streaming over gzipped transport (#2710)
Notifications & Webhooks
- ntfy notification delivery failures resolved (#2556)
- ntfy save button race during protocol check (#2559, #2561)
- Webhook template function registration — custom templates now have access to the shared function library and case-insensitive field aliases (#2505)
- Discord webhook no longer returns 400 for warnings — webhook provider skips non-detection notifications when a custom template is configured (#2530)
- Webhook delivery error suppression — per-provider failure tracking with periodic reminders and recovery reporting (#2536)
- Webhook error reporting — connection error tracking no longer masks real delivery errors (#2584)
- MQTT phantom
ClipName— cleared when the clip was not actually saved to disk (#2530) - MQTT publish suppression during disconnects — aggregate reporting on reconnect instead of flooding Sentry (#2535)
- Alerting engine v2 schema guard and UX polish (#2510)
Weather
- Localize Daily Activity weather tooltip temperature (#2681)
- Always request metric units from Weather Underground API regardless of display preference (#2686)
- Timezone derived from coordinates instead of system TZ — sunrise/sunset and weather timestamps respect the configured location even when the host timezone differs (#2689, #2735)
- Sunrise/sunset displayed in local timezone — converted from UTC, populated via suncalc in
SaveWeatherData(#2542) - Weather API 401 backoff — exponential backoff with retry cap to stop flooding Sentry when credentials are invalid (#2535)
- Weather "daily events not found" retry — brief delay on transient DB lock instead of hard failure (#2536)
Dashboard & Frontend
- Safari infinite reload loop resolved — IPv6 zone ID parsing no longer trips Safari (#2621)
- Mobile drawer closes after tapping a menu item (#2549)
- Audio settings popup sliders — no longer trigger spectrogram selection when adjusting (#2550)
- Range filter species count resets when opening the view species popup (#2551)
- Stale SpeciesDetailModal cache cleared on open — prevents flashes of old data (#2588)
- Live audio play button, guest dashboard layout, datepicker overflow — multiple UI fixes (#2547)
- Skeleton loading states in DetectionDetail — prevents layout shift during load (#2635)
- Inline review buttons in search results — verification status updates immediately (#2542)
- Gate Search quick-review UI with review permission (#2586)
- "No audio sources" empty state — replaces infinite loading when no sources are configured (#2733 by @rexxars)
API, CORS & HTTP
- CORS wildcard with credentials — reflects the request
Origininstead of sending literal*whenAllowCredentialsis true (#2530) - HEAD requests return correct status — pre-middleware rewrites HEAD to GET before routing (#2530)
- Detections query parameters silently ignored —
species=,search=, anddate=now auto-infer query type;limitaccepted as alias fornumResults(#2530) - Detection deletion cleans up files —
DELETE /api/v2/detections/:idremoves clip and spectrogram files (#2531) - Route collision on numeric IDs — audio and spectrogram handlers validate numeric IDs (#2529)
- Pagination total count correct —
SearchNotesreturns actual total instead of decreasing with offset (#2529) - Unlock idempotency —
POSTlock returns 200 when no lock exists instead of 500 (#2530) - API logger status code — extracts status from
echo.HTTPErrorinstead of always logging 200 (#2529) - Bidirectional species name resolution in settings UI (#2685)
- IPv6 zone IDs in client IP parsing (#2708)
- Basepath consistency — basepath is now applied uniformly across routes and assets (#2731 by @rexxars)
- Search no longer defaults date to today — cross-date search results work (#2542)
Configuration & Validation
- Percentage parsing robustness — accepts bare numbers, fractional 0–1 values, and whitespace; eliminates thousands of Sentry events from disk cleanup cycles (#2531, #2564)
- sqlite_integrity false alarm on fresh v2 installs — legacy-DB-dependent checks skipped in v2-only mode (#2542)
- API v2 handlers use dependency-injected settings — replaces
conf.GetSettings()calls for predictable behavior across tests and hot-reload (#2608)
Telemetry & Reliability
- Reduce production log noise — quieter logging across 6 modules (#2707)
- Suppress transient signals from Sentry — operational throttling and transient errors filtered (#2728)
- Frontend auth errors filtered from Sentry — HTTP 401/403 suppressed at three levels (#2536)
- Nil
slog.Loggerguard — prevents silent error loss when inner logger is nil (#2566) - AudioCore error reporting improved (#2690)
- Skip journald reads in containers — avoids failing reads on container filesystems (#2727)
- Avicommons license variants — image provider handles all license variant strings (#2727)
- Short-circuit image fallback for exhausted species (#2730)
- EventTracker cleanup — prevents unbounded memory growth (#2529)
- Shutdown safety — 500 ms minimum grace for job queue workers, scheduler stopped before channel close (#2535)
- startup-wrapper.sh SIGTERM handling — signal exits treated as clean shutdown (#2536)
- CPU cache updater timer leak — replaced
time.Afterwithtime.NewTicker(#2535)