Features
Everything BirdNET-NG can do today.
Audio Capture
Deploy microphones anywhere — Raspberry Pi nodes or Android phones — and let them record autonomously.
- 3-second WAV chunks at 48kHz mono (BirdNET's native window)
- Three capture modes: ALSA (real hardware), simulated (sine wave), replay (pre-recorded files)
- Local outbox queue (sql.js SQLite) — never blocks on network, drains with exponential backoff
- MQTT transport with auto-reconnect, QoS 1, and hub-side acknowledgment
- On-device audio filtering: RMS silence gate + YAMNet VAD (per-category bird-likelihood) — rejects silence and non-bird audio before uploading. Per-category satellite-side drop (amphibian / insect / anthropogenic / human voice / other animal) is configurable per tenant and per satellite
- Recording profiles: continuous, dawn chorus, night migration, low power — with sunrise/sunset scheduling from GPS coordinates (NOAA algorithm)
- 30-second heartbeat keepalive (decouples online status from audio sending)
AI Inference
BirdNET deep learning model identifies 6,500+ bird species.
- Geo-aware filtering: species range narrowing based on GPS + date
- Confidence-based re-processing: uncertain detections (0.4–0.7) get a second pass with extended window, frequency isolation, and cross-chunk correlation
- Scalable workers: Python 3.11 + TFLite, scale horizontally with
docker compose up --scale worker=N - Worker dashboard: connected workers, queue depth, processing stats
Species Intelligence
Rich context around every detection.
- Species catalog: sortable cards with detection count, confidence, first/last seen, satellite count, accuracy. Sort by name uses primary language; search works across all 38 languages. Respects the tenant's detection-visibility toggles — hidden categories (frogs, machine noise, …) never appear here or in any list, map, chart, or report
- Species profile pages: 90-day trend, hourly activity, seasonal presence, satellite breakdown, confidence distribution, share and comment on detections
- Species notes + facts (vhub-0.42.0+): an "About" card with the Wikipedia intro in your language (CC BY-SA, linked attribution) plus trait badges — conservation status (shown only when above "Least concern"), wingspan, weight, wing chord, habitat, diet, and migration behavior, combining Wikidata with the AVONET morphology dataset (Tobias et al. 2022, CC BY)
- Expected species: eBird integration cross-references what you should be seeing vs. what you've detected
- First-of-season detection: automatic seasonal tracking with green badge on detection cards
- First-of-day detection: per-species per-satellite daily tracking with blue badge
- Rarity model: frequency-based rarity scoring with watchlist alerts. Effort-aware (v0.32+): the eBird-driven rarity flag carries a
rarity_confidence∈ {high, medium, low} based on nearby checklist count from the same/recentpayload — high ≥50, medium 10–49, low 3–9, no flag below 3. Per Johnston 2021, "no nearby observations" at low-effort sites is just sampling noise rather than evidence of rarity, so badges dim accordingly - Species images: bird thumbnails from Wikipedia, downloaded in background as both 800px display thumbs AND full-resolution originals in MinIO. iNaturalist as opt-in fallback (v0.29+) for gallery extras when Wikipedia is exhausted — license-gated (default = commercial-safe
cc0/cc-by/cc-by-sa); admin can widen to include CC-BY-NC variants. Lightbox shows the license code as a small uppercase badge on the attribution pill
Detection & Timeline
Browse, verify, and explore your detections.
- Detection list: filter by satellite, status, species, confidence — with server-side sorting and pagination. Species search works across all 38 languages
- SpectrogramPlayer: click-to-seek, drag-to-select loop region, auto-boost (peak normalization), speed control (0.25x–2x), volume, download, buffering indicator
- Timeline: chronological recording sessions with inline chunk bars, species filter pills, expand/collapse sessions
- Community verification: vote Yes/No/Unsure with keyboard shortcuts, consensus threshold, accuracy stats. Optional time-boxed annotations (v0.30+): drag a rectangle on the spectrogram marking exactly when + at what frequency the species call is, then cast a vote — captures a labeled bounding-box dataset opportunistically (Merlin-style)
- Calibrated confidence (v0.30+): per-(tenant, model) isotonic regression realigns BirdNET's raw confidence with observed confirmation rates. Detection rows show
Conf 78% (62%)inline once a curve has been fit; refresh from the Models tab. Same approach Cornell's Johnston 2021 paper uses to fix probability bias in species-distribution-model output - Detection comments: inline expandable thread per detection
- Shareable links: HMAC-signed public URLs with species image, translated names, stats (today + total + confidence), and lightbox (no login required)
- Link previews: Open Graph meta tags served for all share URLs — rich previews on Discord, Slack, Telegram, WhatsApp, Twitter, Facebook
- Link previews: Open Graph meta tags for rich previews on Discord, Slack, Telegram, WhatsApp, Twitter, Facebook
Analytics & Visualization
Understand your data at a glance.
- Dashboard: 7-day detection trend, hourly activity bars, biodiversity snapshot, fleet status
- Detection map: detection clusters by default (satellite markers and heatmap as opt-in layers), time range filter — species names in user's language. Custom date ranges (vhub-0.40.0+): pick any start/end in a range calendar (also on Timeline and Compare); large windows render as zoom-refining grid cells whose counts are true totals, and the viewport auto-fits the selected window
- Unified time selector (vhub-0.41.0+): one control — rolling presets (last hour…year), calendar periods (hour…year, incl. quarter), and custom ranges down to the minute; rolling windows stay live, plus an "All time" choice that removes time filtering entirely
- Shared time context (vhub-0.43.0+): the selected window follows you across Timeline, Map, Compare, Analytics, and both Detections views — same control, same spot (right end of the toolbar) on every page. Filters and sorts on every page also survive refresh and navigation
- Biodiversity indices: Shannon H', Simpson 1-D, Pielou's evenness — per satellite and global
- Migration patterns: month-by-species heatmap showing seasonal presence
- Weather correlation: Open-Meteo temperature + precipitation overlaid on detection activity
- Location comparison: side-by-side satellite stats, Jaccard similarity, shared/unique species, hourly activity
- Activity heatmaps: hour-by-day-of-week grids per satellite and globally
Smart Alerts
Get notified about what matters.
- Three trigger types: detection (species, confidence, time window, rare/first-of-season/first-of-day), absence (no activity for N minutes), trend (diversity or count drop/increase)
- Six notification channels: Email (SMTP), Slack (Block Kit), Telegram (Bot API), Google Chat (Cards v2), Discord (embeds), Webhook (
X-BirdNet-Signature: sha256=…HMAC). Webhook signing secrets are AES-256-GCM-encrypted at rest (write-only through the API). The legacy standalone tenant-webhooks table was removed in favour of channels. - Fire modes:
cooldown(re-trigger after N minutes),once_until_clear, andon_state_change. The seeded satellite-offline rule useson_state_changeso a down satellite alerts once per outage and auto-rearms on recovery, not every hour. - Once per species per day option — each species triggers at most one notification per rule per day
- Inline rename for rules and channels, duplicate rules (created disabled)
- Alert messages in tenant's language — species names, labels, and rarity reasons translated based on tenant settings
- Notification links point to public share pages (no login required for recipients)
- Species images in notifications via Wikipedia direct URLs
- Platform health banner: persistent warning when core services are down
- Scheduled exports: cron-based automatic CSV/JSON/eBird/iNaturalist delivery via any channel
Data Export
Get your data out in standard formats.
- CSV and JSON export with filters
- PDF detection report (vhub-0.41.0+): a print-ready report for any time window — summary stat cards, site map, activity chart with offline shading, and a species list with smart-cropped thumbnails, names in all your configured languages (with language shortcodes), confidence stats, and the towns/countries where each species was heard. A4 or US Letter (Account preference), English/French
- Composable report sections (vhub-0.43.0+): a PDF icon in the app header opens the report builder from any page — pick any mix of site map, activity chart, weekly pattern heatmap, weather correlation, biodiversity indices, species list, and species notes (Wikipedia intro + traits, vhub-0.42.0+); the report covers the shared time window and an optional satellite scope
- Scheduled PDF reports (vhub-0.43.0+):
pdfas a scheduled-export format — a recurring report of everything since the last run, attached to email or delivered via webhook - eBird checklist generation
- iNaturalist observation export
- xeno-canto recording submission
- Scheduled automatic exports with cron scheduling
Satellites & Fleet Management
Monitor and configure your entire network.
- Fleet list: sortable, searchable, filterable — click to open dedicated satellite detail page
- Satellite detail page: full-page view with stats cards (status, recording, version, location, CPU, storage, uptime, last seen), today's hourly activity chart, 24h schedule bar, filter stats, editable config, biodiversity, expected species (scrollable, with links to species profiles), live audio, actions
- Telemetry: CPU, storage, battery, uptime, GPS, recording state — displayed as stat cards
- Per-satellite config overrides: inline editor with lock toggle, customize/reset, override markers
- Recording profile push: assign and push profiles via MQTT, integrated in schedule section
- Remote update: trigger Pi satellite updates from the web UI via MQTT, with status feedback. Supports HTTPS token or SSH key authentication via
.env - Audio device autodetection: automatically discovers the ALSA capture device when
AUDIO_DEVICEis not set - Error state reporting: satellite reports "error" heartbeat after consecutive capture failures, recovers automatically
- Filter stats: aggregate tracking of chunk filter results (processed/sent/rejected by reason) in satellite detail panel with pass rate bar
- Per-session filter rate: timeline sessions show "X% sent" pill when significant filtering occurs
- Satellite registration: register from web UI with complete
.envgeneration - Schedule preview: resolved sun times and recording windows per satellite
- Live audio: "Listen live" streams unfiltered real-time audio via hub-signaled MQTT — satellite sends all chunks (bypassing filter) while active, with 60s auto-stop timeout. Continuous playback with SpectrogramPlayer auto-chaining
- Force update: always-visible button on satellite detail page (bypasses version check for dev/testing)
- Disk management: WAV files deleted immediately on hub acknowledgment, periodic outbox DB purge
Android App
Turn any phone into a satellite. UI parity with the hub web (same Mantine v9 theme), bilingual EN/FR.
- Continuous native capture (vapk-1.5.0+): a native thread holds one AudioRecord open and slices gapless 3 s WAVs onto an on-disk queue; JS drains it when it gets CPU. Survives WebView throttling with the screen off, where the old per-chunk JS loop dropped audio.
WebView.resumeTimers()on the AlarmManager keepalive un-throttles the drain. - Detection-first UI (vapk-1.6.0+): three tabs. Live opens by default with an ambient listening pulse driven by the live chunk RMS, a hero card for the latest detection (species photo, confidence ring, rare / first-of-day badges), and today's species list grouped by count and last-heard. Monitor keeps the technical surface (icon stat cards, chunk audit, schedule, outbox storage, diagnostics + log viewer). Settings holds device behaviour toggles. The persistent notification mirrors the latest detection.
- Live spectrogram (vapk-1.11.0+): a scrolling spectrogram of the current recording on the Live tab, under the listening pulse. The native capture thread runs a 2048-point FFT (Hann window) per window and emits 64 frequency columns (0–12 kHz) on a
spectrumCapacitor event; the canvas scrolls one column per window (~23/s) with an on-brand green ramp. Streaming is gated bystartSpectrum/stopSpectrumand pauses on screen-off / backgrounding, so it never burns battery while the capture thread keeps running unattended. - On-device inference (vapk-1.12.0+): when the hub sets a phone to
deviceinference mode, chunks that pass the YAMNet gate are classified locally by a bundled BirdNET v2.4 model (TensorFlow Lite). Only hits upload (audio + detections together on theinference-resultchannel); no-detection chunks drop on-device. Hits go through the same store-and-forward outbox, so they survive an offline hub. If the model can't load, the phone falls back to uploading audio for hub inference. The Live screen shows an "On-device AI" / "Hub AI" badge, a "Hub-managed" chip when config is locked, and a "Hearing a bird… / Heard:" pulse on the spectrogram. Device results honor the tenant's minimum confidence and the lat/lon/week range filter (vapk-1.13.0+, via a bundled BirdNET meta model), and bird names render in the tenant's primary language. - Design system (vapk-1.14.0+): the satellite app has its own dark-first theme (emerald primary, caramel secondary, a 3-stop confidence scale), a sticky header with a dark/light toggle, a "Rings" listening pulse, an interactive OpenStreetMap site picker in Settings (zoom + drag the pin; placing it manually disables GPS), and Monitor/Settings laid out per the design handoff.
- Offline-first recording (vapk-1.8.0+): an unreachable hub never blocks the session. Capture starts before any network (mic live <1 s after launch, vapk-1.10.0+); chunks land in a persistent sql.js + Filesystem outbox first and upload when connectivity returns. The same three-pass purge as the Pi (age / hard cap / soft-cap acked-only) bounds storage locally. "Start recording when the app opens" toggle for unattended field installs.
- Reliable upload pipeline (vapk-1.10.x): paced one-chunk-at-a-time drain (waits for the broker PUBACK) so a backlog can't flood and kill the socket; a persistent MQTT session (
clean: false) so the broker queues hub acks across brief reconnects instead of dropping them; client pings disabled (the throttled-WebView keepalive watchdog was self-killing healthy sockets), with half-open detection moved to PUBACK timeouts. The queue now drains fully to zero. - Notification lifecycle (vapk-1.8.0+): the foreground service (and its notification, wake/wifi locks, mic) exists only while recording; pausing or swiping the app away releases everything cleanly.
- Foreground service with AlarmManager keepalive, GPS gate (background fix, last-known coords until it lands), live audio streaming, hub filter config over MQTT, device-side recording-profile picker (hub-lockable).
- MFA login (vapk-1.4.1+): TOTP and backup-code prompts after the password step.
- Satellite-scoped API key (vapk-1.4.0+): registration mints a non-expiring satellite-scoped key, enforced server-side since vhub-0.37.1 (a key can only touch its own satellite). The legacy session JWT is auto-exchanged on first launch after upgrade.
- In-app updates via the hub's
/api/mobile/latestendpoint; APK download surfaced on the hub's Satellites page for new-phone onboarding.
Multi-Tenancy & Auth
Multiple teams share infrastructure with full data isolation.
- Row-level isolation via
tenant_idon all data tables - Global users with per-tenant membership and roles (viewer < member < admin < owner)
- Platform admin: cross-tenant access, hidden from tenant member lists
- JWT cookie sessions (httpOnly, 7-day expiry) + Bearer API key auth
- Invite system: single-use links with role assignment, 72h expiry
- Login security: rate limiting (20/IP/15min), account lockout (5 failures), last login tracking
- Tenant-scoping enforced on every UUID-addressed route (vhub-0.36.4+): detail/audio/satellite/field-note reads and the create routes re-anchor authorization on the resource's own tenant (not the caller's resolved tenant), answering 404 so foreign UUIDs can't be probed. Vote/annotation identity comes from the session, not the request body.
- Regression test suites lock this in: a tenant-isolation matrix and a security-hardening matrix run against the real app over an ephemeral Postgres.
Multi-Factor Authentication (v0.27+)
Optional second factor for any user, mandatory for platform admins.
- TOTP with QR-code enrollment — works with Google Authenticator, 1Password, Authy, etc. Secret AES-256-GCM-encrypted at rest.
- WebAuthn passkeys — Touch ID, Windows Hello, YubiKey, etc. as an alternative second factor on devices that support them.
- Backup codes — 10 single-use codes shown once at enrollment, copy/download/regenerate from
/account. - Forced enrollment for platform admins before they can access the platform; CLI emergency reset escape hatch for the lost-device-and-codes case.
- Remember this device for 30 days — opt-in cookie skips the second factor on the same browser; per-device list with revoke on
/account. - Audit coverage: every MFA action (enable/disable, login_failed, passkey_added/removed, reset_by_admin, …) appears in the audit log.
User Management & Settings
Full control for admins and users.
- Account page: edit profile, change password, delete account
- User preferences: primary/secondary bird name languages, date/time format, voting mode
- Platform users: search, block/unblock, reset password, add to tenant, edit, delete
- Tenant settings: confidence thresholds, audio filter config, heartbeat interval, offline timeout, retention, watchlist, tenant language
- Platform settings (v0.29+): split into 5 hash-deep-linked tabs — Access · Images · Storage · Models · Mobile. Spotlight has one entry per tab so "Wikimedia" / "Retention" / "APK" / "iNaturalist" jump straight to the right tab.
- Access: registration / tenant-creation / invite-link toggles
- Images: cache stats with per-source breakdown (Wikimedia + iNaturalist; primary + extras), Wikimedia API config (contact email + OAuth token + test connection + hourly usage bar), iNaturalist toggle + license filter + per-minute usage bar
- Storage: three cards. Storage overview (total bar with soft + hard cap markers, ✓/⚠ state hint, cap inputs). Audio (protected/unprotected split bar, 4 chunk stats, retention rules — days / keep-best / silent hours, schedule + run-now). Other caches (Images / Models / APKs proportional bar, gallery extras + APK keep-N).
- Models: BirdNET classifier registry + Perch v2 embedding registry + Inference Mode (single / A/B compare with sample rate)
- Mobile: APK upload + release list (force-update toggle, delete with storage cleanup)
- Protected audio guarantee: keep-best + pinned chunks are NEVER auto-deleted, even at hard cap (v0.29 correctness pass)
- Cross-tenant Members + Alerts inbox (v0.29+): platform admins on "Toutes les organisations" see Members and Alerts inbox spanning every tenant with an "Organisation" column; Members invite form gains a tenant Select picker. Tenant admins still see only their tenant
- Audit log: 30+ action types covering login, registration, profile changes, satellite management, settings, votes, webhooks, cache operations — with real client IPs
- GDPR rights (vhub-0.38.0+): Export my data on the Account page downloads a JSON bundle of everything held about you (profile, memberships, votes, comments, annotations, field notes, MFA status, audit entries; no secrets). Account deletion cascade-wipes your child rows and anonymizes your audit-log entries in place (email + IP stripped, trail integrity kept).
- System monitoring: platform admin view of all running service instances (API, dispatcher, workers), infrastructure health, data summary, queue stats
- Tenant ownership transfer
Internationalization (i18n)
The full interface in your language.
- English and French translations with 1500+ keys covering every page (placeholder substitution via
t(key, default, params)) - Language switcher in sidebar
- Locale-aware formatting: relative times, date/time, language names via Intl APIs
- Singular/plural support via
Intl.PluralRules(e.g. "1 detection" vs "5 detections") - Bird names in 38 languages via BirdNET label files (primary + secondary display)
- Spotlight cross-language species search (v0.29+): every detected species in the active tenant appears in Spotlight (Cmd/Ctrl+K) with
keywords[]carrying every translation already loaded for the user's primary + secondary languages, plus EN common name + Latin + species_code - Map popups and heatmap labels localized
- Start-of-week preference (Monday/Sunday/Saturday) for heatmap grids
Per-Stream Versioning (v0.28+)
Hub, Pi satellite, and mobile app version independently — backend churn no longer pings every satellite with phantom "update available" prompts.
- Three SemVer streams:
hub(vhub-X.Y.Z) /satellite(vsat-X.Y.Z) /mobile(vapk-X.Y.Z) - Per-scope bumping:
pnpm version:bump --scope <hub|satellite|mobile> - Per-stream comparison on Satellites page: Pi satellites compare against the latest
satelliterelease, mobile satellites against the latestmobilerelease. Hub-only bumps don't trigger a "needs update" badge anywhere GET /api/system/versions: returns{hub, satellite, mobile}— latest target per stream- Pi update flow:
birdnet-update.shchecks out the latestvsat-*tag; hub-triggered updates work unchanged - Mobile versionCode: deterministically derived from SemVer (
major*1_000_000 + minor*1_000 + patch) — every released APK is now a real version bump, no more+b<timestamp>suffix
Infrastructure
- Docker Compose: 8-container stack (9 with split mode) behind Traefik reverse proxy
- Hub split mode: stateless API (scalable) + single-instance dispatcher
- Four FQDNs: web, MQTT, MinIO console, documentation site
- MQTT over WSS (port 443, no extra firewall ports)
- Service registry: Redis-based auto-discovery with System monitoring page
- 80 database migrations, run automatically on hub startup
- Deep health checks (vhub-0.37.0+):
/api/healthis a cheap liveness stub;/api/health/readyprobes Postgres, Redis, MinIO, and MQTT with per-check timeouts and returns 503 when any is down. Docker healthchecks poll it (api), with heartbeat-file probes for the dispatcher and Python workers. - Inference worker hardening (vhub-0.37.0+): pooled Postgres connections, exponential retry backoff so a poison job can't tight-loop, LRU-capped range-filter cache, pre-download size guard, persistent model cache volume, cpu/memory limits. Plus the self-healing exits (vhub-0.32.8+) on Redis loss or a stalled processed-counter.
- Idempotent re-ingest (vhub-0.37.4+): a re-sent chunk (lost ack) is acked without re-running inference. Detections are timestamped by recording time, not analysis time (vhub-0.37.5+), so store-and-forward backlogs land in the right place on the timeline.
- Bounded job retention: finished BullMQ jobs are trimmed (24 h completed / 7 d failed) so Redis doesn't grow without limit.
- Test suites: hub (routes + tenant isolation + security hardening over an ephemeral Postgres), Pi and mobile outbox lifecycle + purge.
- Backup sidecar (vhub-0.38.2+): nightly rotated
pg_dump+ incremental MinIO mirror + Mosquitto credential copy, local then off-site via rclone. Restore script + runbook (deployment/backup-restore.md); Redis intentionally excluded. - Versioned documentation site with version switcher