## 0.5.15.b — 2026-10-19 — UX — Troubleshooting links - Settings → Diagnostics and the feedback form link to the troubleshooting page. - Long commands and addresses on the help pages wrap on phones instead of running off the screen. ## 0.5.15.a — 2026-10-19 — FEATURE — Troubleshooting page; recent recordings on Apple Watch - New troubleshooting page (ourchirp.com/troubleshooting.html): about 50 fixes across the app, notifications, Apple Watch, stations, sound, detections and running your own server, with search and topics. - Apple Watch (beta): Recent recordings lists the site's five newest and plays them on the watch (server: GET /watch/recent and /watch/audio/{id}). - The home page no longer scrolls sideways on phones. ## 0.5.14.z — 2026-10-19 — DEVELOPER — Docker Hub build fixed - The Docker Hub build failed on arm64 (no tensorflow-cpu for ARM); ARM now uses tensorflow, and the chirp image is built for amd64 only until arm64 is tested. - Docker Hub overviews for the three images (unraid/dockerhub/), set by the publish workflow. ## 0.5.14.y — 2026-10-18 — UX — Only Back restores the Recordings list - Back (or Forward) returns Recordings to the same page, filters and scroll; opening it from the tab bar, menu or a link starts on page 1 with no filters (grid/list view and page size are kept). ## 0.5.14.x — 2026-10-18 — UX — Recordings list remembers where you were - Leaving Recordings and coming back keeps the page number, filters (station, time of day, saved, date range), search and scroll position. ## 0.5.14.w — 2026-10-18 — BUGFIX — Maps stay under the tab bar - The recording and range maps no longer draw over the bottom tab bar and header when scrolled. ## 0.5.14.v — 2026-10-18 — UX — What's New popup brought up to date - The What's New popup shows again, covering per-bird AI ratings, the Low pitch view and Back/Forward. ## 0.5.14.u — 2026-10-18 — FEATURE — Low pitch view for owls - Recordings with an owl or another low caller open with low notes stretched (mel spacing), so their calls get room instead of a thin line at the bottom. - A Low pitch toggle above the spectrogram switches any bird recording between that and even pitch spacing. ## 0.5.14.t — 2026-10-18 — ENHANCEMENT — Profile photos on notes - Notes, All Notes and the dashboard's Recent comments show each author's profile photo instead of their initial. ## 0.5.14.s — 2026-10-18 — BUGFIX — Profile photos show after upload - Uploaded profile photos never showed (the address put the file extension into the user ID, so it was never found); a new upload also replaces the cached old photo. ## 0.5.14.r — 2026-10-17 — BUGFIX — Maps without the CARTO watermark - The recording and range maps use a CARTO key (CHIRP_CARTO_KEY, also in the Unraid template); CARTO now marks tiles "API key required" without one. ## 0.5.14.q — 2026-10-17 — UX — Station versions: Chirp stations only - Changelog → Device no longer lists Bird Buddy feeders and Haikuboxes, which don't run Chirp's station software. ## 0.5.14.p — 2026-10-17 — BUGFIX — Notifications only from the server the app shows - Change server in the iPhone app now takes the phone off the old server's push list first; going back adds it again. ## 0.5.14.o — 2026-10-17 — BUGFIX — No duplicate notifications from a retired server - New setting CHIRP_NOTIFICATIONS_OFF=1 stops a server's pushes, emails, journals and digests; on for the retired chirp.joshuasalvimd.com server, which was sending a second Daily Journal. ## 0.5.14.n — 2026-10-17 — ENHANCEMENT — AI Second Opinion: one row per bird - One row per detected bird, each with its photo and its own rating on the scale; the AI's full read is under More. - The AI now gives each bird its own verdict, and the audio check runs for every bird, not just the top one. ## 0.5.14.m — 2026-10-17 — BUGFIX — Changelog history restored; station versions - The Changelog page shows every release again (releases since 0.5.14.b had replaced the history). - Changelog → Device lists each station's installed version and whether a newer one exists. ## 0.5.14.l — 2026-10-17 — UX — Logo back at top left; Back goes up to five pages - On phones the Chirp logo is back at the top left, with the back and forward buttons after it. - Back goes up to five pages. ## 0.5.14.k — 2026-10-17 — FEATURE — Back and forward buttons; v3 drops station night callers it doesn't confirm - Back and forward buttons at the left of the top bar, on every page and in the iPhone app; greyed out when there's nowhere to go. - With BirdNET v3, station detections v3 didn't confirm are removed, night callers included (a 35% daytime Common Nighthawk stayed before). ## 0.5.14.j — 2026-10-17 — ENHANCEMENT — AI Second Opinion rating on a scale - The rating shows as a four-step bar from Likely wrong to Likely right, with only the reached step lit and labelled. ## 0.5.14.i — 2026-10-17 — BUGFIX — Detection rows fit on phones - A recording's detection rows wrap onto two lines on narrow screens: time and confidence, then Jump, Clip and Enhance, nothing overlapping or cut off. ## 0.5.14.h — 2026-10-17 — ENHANCEMENT — What's New for this week's changes - New What's New popup: AI Second Opinion's single rating, its audio check for short calls, and BirdNET v3's minimum applying to station detections. ## 0.5.14.g — 2026-10-17 — BUGFIX — Audio check hears short calls - AI Second Opinion's audio check now measures short calls (chips, "pik"s, 20–50 ms) too; faint Downy Woodpecker and chickadee calls at 6–8 kHz read as "nothing above the background" before. - The AI gets those measured frequencies, so it no longer guesses them from the image. ## 1.4.4 (chirp-device) — 2026-10-17 — BUGFIX — Hum check logs both mains families - Each hum check logs the 60 Hz and 50 Hz readings; 50 Hz is chosen only when it leads by 3 dB (weak hum read as 50 Hz in Massachusetts). ## 1.4.3 (chirp-device) — 2026-10-17 — BUGFIX — Hum filter stays on - Once on, the hum filter turns off only after two checks in a row find the hum under 5 dB (one quiet check had let it off for 5 minutes at a time). - Each check logs how far the hum stands above its neighbours, to compare wiring changes. ## 0.5.14.f — 2026-10-17 — BUGFIX — No station detections below the v3 minimum; tidier color picker - With BirdNET v3, when the server pass finds nothing, the station's own (BirdNET 2.4) detections are removed too, as they already were when it found something; night callers are still kept. - Detection colors: one color strip per palette that fits the card, shorter descriptions. ## 0.5.14.e — 2026-10-16 — ENHANCEMENT — AI Second Opinion gives one overall rating - The card opens with one rating (Likely right, Probably right, Uncertain or Likely wrong) that combines the AI's read of the spectrogram with the audio check. - Audio check findings are listed under the rating; the AI's full reasoning folds under "Why". ## 0.5.14.d — 2026-10-16 — BUGFIX — Request form choices; Getting Started Back to top - About page: the Request access choices show the dot beside the text. - Getting Started: Back to top works on phones (the cards no longer shift when tapped). ## 1.4.2 (chirp-device) — 2026-10-16 — FEATURE — Mains hum filter - Notches mains hum (60 or 50 Hz and harmonics to 1 kHz, 3 Hz wide) on each mic before detection, recordings and night audio. - audio.hum_filter: auto (default; on only when hum stands 10 dB out, rechecked every 5 minutes), 60, 50 or off. ## 0.5.14.c — 2026-10-16 — ENHANCEMENT — Clearer model settings; chirp.joshuasalvimd.com retired - Detection Model: v3's minimum confidence sits under the v3 choice and says what it does. - BirdNET Analysis is now BirdNET 2.4 Settings and shows only when 2.4 is the model; defaults in its hints corrected (5%, 2.5, 1.5). - Retired hostnames redirect to the new address (CHIRP_REDIRECT_HOSTS, CHIRP_REDIRECT_TO); API calls get 410. chirp.joshuasalvimd.com now goes to ourchirp.com. - The station config sends uploads and heartbeats to ourchirp.com only. ## 0.5.14.b — 2026-10-16 — ENHANCEMENT — Softer Dashboard photo edge - The Live card's photo fades only slightly at its edges. ## 0.5.14.a — 2026-10-16 — FEATURE — BirdNET v3 by default, saved recordings, one Enhance control - BirdNET v3 is the default model, with owl checks (a hoot at the owl's pitch; 80% in daylight); BirdNET 2.4 selectable in Settings → Detection. - AI Features and Bats have their own Settings tabs; Experimental keeps Nighthawk, range maps and the heatmap. - Settings search finds every card, tab and setting. - Recordings filter by time of day (site time zone) and by Saved; saved recordings skip automatic archive and delete. - One Enhance control on recordings and calls: Original, Enhanced (low cut and noise reduction) or AI. - Night Calls by night, week or month, grouped by family; each call has its own page with a large spectrogram. - Confidence badges match the Recordings list (green for high). - Phone ⋯ menu in three groups; Dashboard photo fades into the card. - Guide pages' header matches the About page; Getting Started and User Guide updated. - Docker Hub workflow, .dockerignore and Unraid Community Applications templates. ## 1.4.1 (chirp-device) — 2026-10-16 — BUGFIX — No gaps in the detection audio - The mic mixer waits for each primary chunk instead of filling 0.15 s timeouts with silence (about half of every second was silent). - Fixes choppy night audio, "-inf dBFS" levels and missed calls on the detection path; recordings were unaffected. ## 0.5.13.e — 2026-10-16 — BUGFIX — Night Calls playback and labels - Play works: a page of thumbnails no longer uses up the database connections; clips play as WAV. - Spectrograms are made when a call is found, not when the page opens. - Sound-alike groups are named for what they could be ("Swamp or Lincoln's Sparrow", "Warbler, one of 13") and list every species. - Calls only placed as "songbird" are hidden behind a button; a "How sure is each call?" key explains each level. ## 0.5.13.d — 2026-10-15 — ENHANCEMENT — Clearer Settings text - Empty Recordings: plain names for the two counts ("show nothing", "nothing from the server") and both delete buttons. - Settings descriptions shortened and reworded throughout. - AI Second Opinion uses that name in Settings too. ## 0.5.13.c — 2026-10-15 — BUGFIX — Re-analyze with BirdNET v3, Tailscale on Unraid - Re-analyze runs BirdNET v3 when it's on, both to compare and as the main model. - AI Second Opinion no longer fails when Re-analyze replaces the detections mid-run. - Tailscale socket moved off the Unraid share; the hourly mover broke it every hour. - tailscaled gets a state directory (stops the tailnet-lock log spam) and its log is trimmed past 20 MB. ## 0.5.13.b — 2026-10-15 — ENHANCEMENT — Station offline alert delay - Station offline alerts wait 15 minutes by default; set the delay in Settings → Alerts & Email. - Station alerts go only to signed-in admins' devices. ## 0.5.13.a — 2026-10-15 — FEATURE — BirdNET v3 and night flight calls (experimental) - Settings → Experimental → Detection Models: compare each recording with BirdNET v3, or use it as the main model. - Recording pages show a BirdNET v3 comparison card when comparing is on. - Night flight calls: stations upload night audio and Nighthawk lists migrant calls on a new Night Calls page. - Clearing all notifications asks first; deleting an account needs "delete" typed. - Wikipedia searches for the AI journal send a contact URL, as Wikipedia asks (it was answering 403). - Tailscale goes straight to userspace networking after kernel TUN is refused once. ## 1.4.0 (chirp-device) — 2026-10-15 — FEATURE — Night audio for Nighthawk - Uploads 10-minute 22.05 kHz FLAC chunks during the night hours a server asks for (GET /night-calls/accepting); off for servers that don't. - Backlog capped at night_audio.max_backlog_gb (3 GB); needs soxr. ## 0.5.12.ae — 2026-10-14 — BUGFIX — Watch sessions saved to Recordings - Stopping Live Detect on the watch saves the session to Recordings with its full audio, as the phone's Save session does. - The watch sign-in can save Live Detect sessions. - Live Detect is no longer marked Beta. ## 0.5.12.ad — 2026-10-14 — ENHANCEMENT — Watch app logo - The watch app shows the Chirp logo at the top; its bars move while listening, as on the Live Detect page. ## 0.5.12.ac — 2026-10-14 — BUGFIX — Apple Watch sign-in - The iPhone app now sends the watch its sign-in; the watch link never finished starting before. ## 0.5.12.ab — 2026-10-14 — DEVELOPER — Watch plugin check - Logs which native plugins the iPhone app reports when the watch link is missing. ## 0.5.12.aa — 2026-10-14 — BUGFIX — Apple Watch sign-in check - My Profile → Apple Watch shows whether a watch is ready and sends it the sign-in again. - Each watch sign-in attempt is logged on the server to help find why a watch isn't signed in. ## 0.5.12.z — 2026-10-14 — FEATURE — Apple Watch groundwork - Apple Watch app for Live Detect (ios-watch/), built into test builds with WATCH=1; not in the App Store version yet. - POST /api/v1/auth/watch-token: a 30-day sign-in that only works for Live Detect; the iPhone app passes it to the watch. ## 0.5.12.y — 2026-10-14 — ENHANCEMENT — iPhone app versions - Changelog's iPhone App tab lists only uploaded app builds; server-only releases leave the app version unchanged. - Each app version lists the Chirp releases it brought; build-ios.sh records new builds in appReleases.json. ## 0.5.12.x — 2026-10-14 — ENHANCEMENT — Station offline alerts - Admins can turn on Settings → My Notifications → Stations: a push and/or email when a station sends no heartbeat for 10 minutes, and another when it's back with how long it was down. Off by default. - Checked every minute per site (services/device_alerts.py); Haikubox and Bird Buddy are skipped, and stations silent for over a day before the check are not announced. - New columns: users.notify_device_offline_push, users.notify_device_offline_email, devices.offline_since. ## 0.5.12.w — 2026-10-14 — ENHANCEMENT — Menu label - About page menu says "Set up", matching the guide pages. ## 0.5.12.v — 2026-10-14 — ENHANCEMENT — Setup guide and user guide - New /setup.html: station parts and Pi setup, install, three ways to connect (approval email, Discover → Auto-configure, manual key), testing, hosted or self-hosted server, HTTPS and push, app and invites, troubleshooting. - New /guide.html: every page, Settings tab by tab, devices, people and roles, My Profile. - Linked from About (nav, cards, FAQ, footer), Support, sign-in, Settings, Tutorial, Devices, My Profile, the approval and relay emails, README and install.sh. - chirp-server-instances/docker-compose.simple.yml and .env.simple.example: one container for running your own server. - chirp-server (single site) marked deprecated in its README and the guides; move with Import from Database File. - Users page shows the site's invite code with Copy invite (site admins could only see it in the approval email). - Approval email: correct service name (chirp-device), venv step and guide links. - About, Support and Privacy load the full icon set with version tags, so browsers drop the old cached icon. ## 0.5.12.u — 2026-10-13 — BUGFIX — Support and About links open in the browser - My Profile's support page link and the sign-in screen's "What is Chirp?" open in Safari (a new tab on the web) instead of inside the app. - The What's New popup shows again only when its text changes, not on every small fix. ## 0.5.12.t — 2026-10-13 — ENHANCEMENT — Contact form and feature ideas - Support page has a contact form (question, problem, feature idea) instead of an email address; the About page links to it. - My Profile → Help & Feedback sends a feature idea, problem or question from the app (POST /api/v1/support, rate-limited). - Messages are emailed to the server's admins and, on ourchirp.com, listed in Global Admin → Requests with Mark done. - About page: the station → server → app diagram shows its arrows again, nav links line up with Sign in, and every card ends in the same pair of buttons (in-page action, plus GitHub links that open in a new tab). - App Store link placeholder in public/site.js; setting CHIRP_APP_STORE_URL there shows the App Store buttons on About and Support. ## 0.5.12.s — 2026-10-13 — ENHANCEMENT — One password, shorter What's New - Joining a site needs only the invite code; the separate location password is gone from sign-up, Join, New Location, Import and Global Admin → Add member. - Changing a password (My Profile, or an admin resetting one) changes the sign-in password and the copy on every site. - What's New popup: at most three short items, earlier releases of the current version collapsed. - Changelog: major versions (0.5, 0.4…) as cards with a one-line summary, each minor version with its own line; summaries live in lib/whatsNew.js. ## 0.5.12.r — 2026-10-13 — BUGFIX — Live Detect on http:// servers - Live Detect on a plain http:// server address (e.g. a home-network IP in Chirp β) said microphone access was off; iOS and browsers give http:// pages no microphone. It now says the server needs an https:// address, and the app's permissions card shows the same. - The picker's custom-server note says Live Detect needs https://. ## 0.5.12.q — 2026-10-13 — ENHANCEMENT — Server picker test build - PICKER=1 ./build-ios.sh --testflight builds "Chirp β": same app, starts on ios-launcher/index.html to choose ourchirp.com or a custom server (address or IP:port), uploaded for internal testers only. - Picker builds allow any server, plain http:// on the home network (with iOS's local-network prompt), and send links to other sites to Safari; the release build is unchanged. - In picker builds, the sign-in screen and My Profile → Server have "Change server"; tapping a relayed push opens the server it came from. - Push relay requests: the server address is optional; the relay never connects to the server. ## 0.5.12.p — 2026-10-12 — ENHANCEMENT — Site requests and push relay - ourchirp.com/about takes requests for a hosted site or a push relay token; ourchirp.com's front page opens /about for visitors who aren't signed in. - Global Admin → Requests: approving a hosted site creates the account, site, location and device key, and emails sign-in and device setup steps. - Push relay (POST /api/v1/push-relay/send): self-hosted servers send iPhone pushes through ourchirp.com with a relay token; Global Admin issues and revokes tokens. - iPhone push uses the site's APNs key, then a server-wide key from CHIRP_APNS_* (never shown to site admins), then the relay. - Settings → Alerts & Email → iPhone Push shows how pushes are sent and takes a relay token; own-key fields moved under "Use your own APNs key". - About page links to GitHub and lists the three ways to start. - Code is public at github.com/joshsalvi/chirp under the PolyForm Noncommercial 1.0.0 license; scripts/publish-public.sh builds that copy without the patent draft, App Store tooling or device keys (chirp-device/config.yaml.public, from scripts/make-public-config.py). ## 0.5.12.o — 2026-10-11 — ENHANCEMENT — About page - Public page at /about (frontend/public/about.html): what Chirp is, how it works, features with app screenshots, setup and FAQ; linked from the sign-in screen ("What is Chirp?"). - App Store marketing URL in docs/app-store-metadata.json is now https://ourchirp.com/about (applies with the next App Store version). - docs/SERVER_PICKER_PLAN.md and a draft ios-launcher/index.html for connecting the iPhone app to any Chirp server; not in any build yet. ## 0.5.12.n — 2026-10-11 — BUGFIX — Headscale admin socket watchdog - Global Admin's Headscale panel timed out: headscale 0.23.0 kept serving clients while its admin socket stopped answering ("context deadline exceeded" from every CLI command). - The node-list wrapper now checks the socket every 5 minutes and restarts headscale after two failures in a row; clients reconnect within seconds. - The wrapper answers requests in parallel, waits up to 15 s for the CLI, and returns the CLI's error (503) instead of a timeout; the server waits 20 s. - App Review reply shortened to 3,467 characters (docs/APP_REVIEW_RESPONSE.md). ## 0.5.12.m — 2026-10-11 — BUGFIX — Fewer false owls - A hooted owl on the band-limited copy also needs a full-band score of 0.30 for that owl (HOOT_FULL_MIN); the strong-owl bar is 0.45 (was 0.40). - Device owls stay only when the server pass finds that owl too, including when it finds nothing else. - An owl from an earlier pass keeps its score on re-analysis only with full-band support on the new audio. - Tested on Natick field recordings: false owls on 231 midday recordings 20 → 1, field Barred Owls 16 of 16, clip set 81 hits / 7 wrong / 0 false alarms (was 81 / 8 / 1). - owl_eval.py: --extra-pos for labeled field owls, --focus grid over the score rules, results per group. - Recent recordings with owls were re-analyzed under the new rules. ## 0.5.12.l — 2026-10-11 — ENHANCEMENT — Same-day digest, Settings reorganized, test emails fixed - Digest push and email each have a separate send time in the site's time zone (default 9 PM); from noon on a digest covers that day so far, earlier times cover yesterday. - Digest days follow the site's time zone (they ran midnight to midnight UTC), and the digest page can show today so far. - Each bird's play button on the digest page opens Recordings filtered to that bird; ?species= links (also from bird profiles) now filter, with a clearable banner. - Settings: new General tab (location, time zone, web address, what everyone sees, dashboard sections); Alerts & Email (digest times, email server, APNs); eBird keys under Integrations; Access & Security (sign-in, device keys, activity log, network); card layout and app install under Appearance. - The unused webhook URL is gone; its switch is now "New-species emails" in the email card. - My Notifications shows when the digest arrives. - Send Test, Test New Bird and Test Digest no longer return 500 and use the same designs as the emails people receive. - Email works with plain SMTP (port 25) and SSL on port 465 whatever the STARTTLS switch says. - Email links point to the full digest page. ## 0.5.12.k — 2026-10-10 — BUGFIX — Faster Live Detect saves, digest sends once per day - Saving a Live Detect session no longer waits behind other Live Detect checks: clip decoding and BirdNET run off the server's request loop, and the saved recording opens as soon as its spectrogram is ready. - The digest records the day it covered (not the UTC date it went out) and sends at the first digest hour after that day ends, so an evening digest hour no longer repeats the same day. ## 0.5.12.j — 2026-10-10 — BUGFIX — Dependable daily digest, delete one notification - The daily digest only went out if the once-a-minute check landed in minute 0 of the digest hour, so some days were skipped; it now goes out once per UTC day at or after the hour (AppSettings.digest_last_sent_on), and a missed one catches up. - Each notification has its own delete button (DELETE /notifications/{id}); the trash icon still clears all. - Settings → My Notifications no longer shows Dashboard Style and Dashboard Sections, which appeared in whichever tab was open when their data finished loading. ## 0.5.12.i — 2026-10-10 — ENHANCEMENT — Daily digest page - The daily digest opens a full page (/digest?date=…) with the day's totals, first-time birds, activity by hour and every species with its photo, first time heard, best confidence and best recording, with previous/next day arrows; it replaces the pop-up. - Older digests in Notifications, including ones saved without details, open the page for their day (new GET /recordings/statistics/digest?date=, from the same numbers as the email digest). ## 0.5.12.h — 2026-10-10 — BUGFIX — Digest pop-up restored - Tapping the daily digest push, or a digest in Notifications, opens a pop-up with that day's totals, new species, busiest hour and every species (restores a separate 0.5.12.f build from 2026-10-09 that the 0.5.12.f release replaced). - The Journal's Yesterday card and GET /recordings/statistics/yesterday-digest from 0.5.12.f are removed; the pop-up replaces them. - Push links are always stored and sent as in-app paths (server, app and service worker); inbox rows keep the digest details for the pop-up. ## 0.5.12.g — 2026-10-10 — ENHANCEMENT — Expandable iPhone app versions - Each version in What's New → iPhone App opens to show that release's changes; the installed version (or the newest) starts open. ## 0.5.12.f — 2026-10-10 — ENHANCEMENT — Report and block notes, digest in the app, iPhone app version - Notes have ⋯ → Report (inbox and push to the site's admins) and Block (hides that person's notes for you); My Profile → Blocked People unblocks. - Notes are credited to the signed-in person, and only the author or an admin can edit or delete one (the server enforced neither). - The morning digest push opens a Yesterday card in the Journal (new GET /recordings/statistics/yesterday-digest) instead of a full web address the app couldn't open. - Notification links to this site open inside the app, including older ones saved with a full address. - What's New has an iPhone App tab listing the iOS version of each release; the popup and Settings show the installed version. ## 0.5.12.e — 2026-10-09 — ENHANCEMENT — Haptic feedback across the iOS app - Haptics on every tap, tab and section change, toggle, picker and slider, with a firmer tap for delete-style buttons. - Haptics for outcomes: settings saved, uploads, copies, shares, sign-in, failed actions, new detections and Live Detect hits. - scripts/asc.py listing pushes App Store text, URLs, categories, review notes and screenshots from docs/app-store-metadata.json. ## 0.5.12.d — 2026-10-09 — DEVELOPER — Distinct iOS version per release - Each release uploads to TestFlight as its own app version: the patch letter becomes part of the third number (0.5.12.d → 0.5.1204). - scripts/asc.py submits each build for beta review before adding testers, and retries Apple errors one tester at a time. ## 0.5.12.c — 2026-10-09 — ENHANCEMENT — Faster app, simpler Settings - The app opens from the last verified sign-in, hides the launch screen right away and loads other pages in the background. - Returning to a page within 30 seconds reuses its data; any change, new detection or notification refreshes it. - Settings on iPhone is a list of sections with a back button; wider screens keep a sidebar with icons. - Settings search lists matches as you type, including settings inside closed cards, and opens the card. - A Save bar appears only when site settings have unsaved changes; edits survive leaving the page, and Discard undoes them. - Viewers' Settings no longer loads site settings; storage, key and device data load when their section opens. - Settings sections renamed: Site Alerts, Access & Keys, Experimental; alert cards are Webhooks, Email (SMTP) and iPhone App Push (APNs). - TestFlight uploads use the App Store Connect API key and give each new build the previous build's testers and a beta review submission (scripts/asc.py). ## 0.5.12.b — 2026-10-09 — ENHANCEMENT — Device location for device Live Detect - Live Detect through a Chirp device saves and uses that device's location (its latest reported GPS, else its config); phone Live Detect keeps using the phone's GPS. - build-ios.sh no longer stops silently when an App Store signing profile (which has no device list) is cached. ## 0.5.12.a — 2026-10-09 — FEATURE — My Notifications, watched birds, journal pushes, Live Detect location - Settings → My Notifications for everyone (the rest of Settings stays admin-only): push for new species, Daily Journal (6 pm), Weekly Journal (Sunday noon) and the morning digest, plus email; linked from the Notifications tab and My Profile. - Watched birds: Notify me on a species page or in My Notifications, every time (at most one per 10 minutes, opens the recording) or a 7 pm daily summary. - Push alerts open what they're about: new species and watched birds go to the recording, journals to the new Journal page. - Live Detect can save the phone's location with its recordings and uses it for BirdNET's likely-species filter ("Use my location", on by default). - The Field Guide shows the site's town instead of its street address; viewers no longer see site-wide Display Preferences or the Save buttons. ## 0.5.11.d — 2026-10-09 — FEATURE — Delete account in the app; App Store prep - My Profile → Delete account (POST /api/v1/auth/me/delete, password required) removes the person's sign-in, memberships, notes, notifications and push registrations; the only admin can't delete themselves. - New support page (ourchirp.com/support.html); the privacy page points to in-app deletion. - iOS builds are iPhone-only (IPHONE_ONLY=0 to include iPad); docs/APP_STORE_LISTING.md has the full App Store and TestFlight text. - With sign-in off, notifications go to the shared inbox even when user accounts exist. ## 0.5.11.c — 2026-10-09 — CRITICAL — Native push signing - services/apns_push.py imported PyJWT ("import jwt"), which isn't in requirements, so every APNs send failed with ModuleNotFoundError and no push ever reached the iOS app. It now signs the ES256 provider token with python-jose (already installed for sign-in tokens); the key text is stripped first. Test: test_notifications_inbox.test_apns_token_is_es256_with_installed_libraries. - The APNs key for team F7N9ZF7U57 (key NM8V6J5ZMF, bundle com.joshuasalvimd.chirp, production with sandbox fallback) is set on the Natick location and on chirp-server. ## 0.5.11.b — 2026-10-09 — CRITICAL — Viewer accounts are read-only; privacy page - The API had no role check on recording changes: a "viewer" could edit, archive, re-analyze, run AI Second Opinion on, or delete recordings, upload, change bird photos and more. AuthMiddleware now refuses every POST/PUT/PATCH/DELETE from a viewer (403 "This account is view-only") except their own account (auth/me/*, login, logout, activity ping), instances/switch|join, notifications, push, diagnostics/client, recordings/ai-warm, recording and bird notes (comments), share links, birds/ensure and live-detect/analyze (main.viewer_may_write). RecordingDetail hides Edit, Re-analyze, AI Second Opinion, Archive and Delete for viewers. - frontend/public/privacy.html (https://ourchirp.com/privacy.html): what Chirp stores, microphone use, who else receives data (Apple for push, bird information services, AI providers when enabled, iNaturalist on request), no ads or tracking, deletion and contact. - docs/APP_STORE_LISTING.md: App Store Connect and TestFlight text, review sign-in notes and the screenshot list. - Tests: tests/test_viewer_readonly.py. ## 0.5.11.a — 2026-10-09 — FEATURE — Notifications tab, iPhone push, TestFlight build - Notifications inbox: InboxNotification table (inbox_notifications), services/notification_inbox.record() called by send_push_to_all for every send (one row per active user passing the same user_filter as the push, or one shared row with sign-in off; 90-day retention), api/notifications.py (GET list with unread count, GET /unread, POST /read-all, POST /{id}/read, DELETE). POST /api/v1/push/test-native sends a test push to the signed-in person's iPhones and records it. - App: Notifications page (/notifications) with per-kind icons, unread dots, clear and refresh, a push-off card with Allow; a Notifications tab in the mobile bottom bar with an unread badge (Field Guide moves to the ⋮ menu) and a sidebar entry with a dot; lib/unread.js refreshes the count every minute, on return to the app and when a push arrives. A push arriving while the app is open refreshes the inbox; tapping a banner opens its page (previously any received push navigated). - First launch of the iOS app (after sign-in): the setup card now asks for notifications and then the microphone by itself, shows the result and closes; the Allow buttons stay as a fallback. - APNs: send_apns_push tries the configured environment, then the other one when Apple answers BadDeviceToken, so TestFlight (production) and Xcode-installed (sandbox) phones both receive pushes from one server; a token is dropped only when both reject it or Apple reports Unregistered. - build-ios.sh: --testflight (paid team: plain bundle ID, App Store Connect export with destination upload; refuses a free team), PUSH=auto (aps-environment entitlement for a paid team, off for a free team), BUNDLE_ID override, ITSAppUsesNonExemptEncryption NO, and the AppDelegate callbacks Capacitor's push plugin needs to receive the APNs token. Device registration also runs for TestFlight (a new team needs a registered device before Xcode can sign anything). - Tests: tests/test_notifications_inbox.py. ## 0.5.10.s — 2026-10-09 — BUGFIX — iPhone sign-in - Usernames matched case-sensitively and untrimmed, and the login inputs let iOS capitalise and autocorrect, so "Joshsalvi" or "annie " failed with valid passwords. auth.find_by_username: exact match first, else a single case-insensitive match (accounts differing only by case stay exact); used by login in both forks. New usernames are trimmed and checked case-insensitively (register, admin create). Login inputs set autoCapitalize="none", autoCorrect="off", spellCheck=false and autocomplete hints; username and email are trimmed before sending. - The failed-login limit (5 in 5 minutes) was keyed by username plus the socket address, which is the Caddy proxy for every request, so one phone's failures locked that username for everyone; the app then showed the 429 as "Incorrect username or password". The key now uses CF-Connecting-IP, else the first X-Forwarded-For hop, else the socket address (auth.client_address), and the login screen shows the "Too many failed login attempts. Try again in N seconds." message. - Tests: tests/test_username_login.py. ## 0.5.10.r — 2026-10-08 — BUGFIX — iPhone header spacing when shifted - With the page shifted (0.5.10.q) the header still added the full 62 pt top inset, leaving ~60 pt of empty space under the status bar. In html.vp-shift, --inset-top is the inset minus the shift plus 32px, which clears the Liquid Glass blur (~90 pt from the top of the screen). client-diag reports once the 500 ms check has settled (the first reading on launch had no inset yet). ## 0.5.10.q — 2026-10-08 — BUGFIX — iPhone page shifted into place - Replaces the 0.5.10.n-p viewport corrections (stretching the shell and moving bottom elements by a measured or predicted gap, plus the touch-based page-offset check), which left the bar too high, pushed it off-screen, or misfired. Per client-diag from the phone (iOS 27.0.1: screen 874, page 812, top inset 62, page drawn from the top), the page is the right size in the wrong place. viewportFix.js now sets --vp-shift to the shortfall (portrait Home Screen or iOS app, top inset present, 16-120 px; height first seen per orientation) and html.vp-shift translates down by it, carrying the fixed header and bottom bar. Per-element --vp-gap offsets, --vp-h and --vp-top are removed. client-diag is sent once per launch. ## 0.5.10.p — 2026-10-08 — BUGFIX — iPhone bottom bar measured, not predicted - 0.5.10.o pushed the bottom bar 62 pt below the screen on iOS 27.0.1. The phone reported screen 874, viewport 812, top inset 62, page at the top of the screen (client-diag): stretching the shell to 874 already moved where position:fixed bottom:0 anchors, and the bar was then moved down by the 62 pt gap as well. viewportFix.js now stretches the shell first, measures a fixed bottom:0 probe and sets --vp-gap only to what is still missing (0-120 px). The page height first seen per orientation is kept, since iOS may report the stretched height afterwards. client-diag reports anchorGap, fixedBottom and the live height, up to three times per launch. ## 0.5.10.o — 2026-10-08 — BUGFIX — iPhone app position check - 0.5.10.n's viewport fix only acted when iOS reported a top safe-area inset. On iOS 27.0.1 the Home Screen app is drawn from the top of the screen with no top inset and a viewport one status bar (62 pt) short, which looks the same as a correct "black" install to the measurements, so nothing changed. viewportFix.js now reads the page's position from the first touch (Touch.screenY - clientY, remembered per viewport height); at 0 it adds the shortfall as top inset (--vp-top, folded into --inset-top) and extends the bottom as before. - POST /api/v1/diagnostics/client logs what the app reports (screen and viewport sizes, insets, page offset, visual viewport, user agent) as "client-diag" in the server log, once per launch in a Home Screen or iOS app, so device-only layout bugs can be checked from the server. ## 0.5.10.n — 2026-10-08 — BUGFIX — iPhone app fills the screen; device night floor kept; iOS re-sign reaches phones over Tailscale - iPhone Home Screen app: back to apple-mobile-web-app-status-bar-style "black-translucent". "black" (0.5.10.m) did not fix it on iOS 27: iOS 26.1+ and 27 still drew the page from the top of the screen with a layout viewport short by the status bar (WebKit bug 301994), so it sat too high with a band under the bottom bar. src/lib/viewportFix.js measures the shortfall (standalone or the iOS app, top inset present, 16-120 px) on launch, resize, rotation and return, and sets --vp-gap/--vp-h: the shell grows to the screen height and the bottom bar, its menu, Settings' save bar, Scroll to top, the permission sheet and the Home Screen notice move down by the gap. The re-add notice for a stale translucent install is retired. - Docker image: a build step imports tensorflow.lite and birdnetlib, so an incomplete install fails the build. The "|| true" after playwright install-deps also hid pip failures; a full Docker disk on Unraid had produced an image without tensorflow/python, and server BirdNET and re-analysis failed with "No module named 'tensorflow.python'". - Device config push (devices.with_night_rule): in "night" and "per_species" modes the server keeps a night_species_min_confidence the device's managed config already has, falling back to 0.10 only when it has none; "global" still sends min_confidence. It used to overwrite it with 0.10 on every pull, which re-opened the 0.10–0.17 "Unknown" flood on the Natick Pi after its floor was raised to 0.20. - ios-resign: RESIGN_EXTRA_DEVICES (UDID=IP pairs) adds phones to netmuxd by IP (its AddDevice extension) before each check, so an iPhone on the same Tailscale tailnet as the server is re-signed away from home. The service can't reach a phone over the plain internet: installs need a lockdown connection to a paired computer. compose passes the variable; README covers setup. - Tests: test_ios_resign.test_add_by_ip_packet (AddDevice frame and plist); test_night_thresholds covers a device's own floor in each mode. Both files now in chirp-server too. ## 0.5.10.m — 2026-10-08 — ENHANCEMENT — Full-screen PWA, night-caller threshold modes, faster AI Second Opinion, new daily email, iOS re-sign service - iPhone home-screen app: apple-mobile-web-app-status-bar-style "black" instead of "black-translucent". On iOS 26 a translucent standalone app gets a full-screen frame but a layout viewport short by the status bar (62 pt), so the fixed bottom nav floated above a black band; the 2.5rem "Liquid Glass" header padding is gone with it. Installs made before this keep the old setting until re-added; HomeIconNotice detects that (top safe-area inset in a portrait standalone app) and shows the re-add steps. - Night-caller thresholds: AppSettings.night_threshold_mode ("night" floors, "global" birdnet_min_confidence, "per_species") and night_threshold_species. birdnet_pass.species_required/uses_night_floor/night_rules shared by the server pass, re-analysis reasons, near-miss defaults, purge protection (owls are only protected while they use the night floor) and device detection retention. GET /near-misses/night-species and a Settings → Detection panel. Devices get night_species_min_confidence (min_confidence in global mode) and night_global_species injected into their config, and are flagged to fetch it when the rule changes (device 1.3.3 honours the list). - AI Second Opinion: Ollama vision requests send think:false (CHIRP_OLLAMA_THINK=1 restores reasoning; falls back for an Ollama without the option); POST /recordings/ai-warm loads the Ollama model when a recording page opens (/api/ps, then an empty /api/generate with keep_alive); spectrogram sent as JPEG (326 KB PNG → 92 KB hosted, 57 KB at 1024 px for Ollama; the PNG re-encode had made it larger); Anthropic system prompt cached (cache_control). - Email: services/email_theme.py, a light layout with the dark header band, prefers-color-scheme dark styles for Apple Mail, logo and bird photos attached inline (cid:), so they show without remote images or a signed-in photo URL. Daily digest (build_daily_digest): species cards with photo, detections, best score, first heard and a bar; new-to-life-list banner; busiest hour; counts at the display threshold. All other emails use the same theme. - Optional iOS re-sign service (chirp-server-instances, compose profile ios-resign): anisette-v3-server, AltServer-Linux NG and netmuxd; re-signs App.ipa on each iPhone seen on the network every RESIGN_EVERY_DAYS (5), with a page on port 8099 for two-factor codes and status. POST /api/v1/ios-resign/event (stack device key) emails the admin when a code is needed or a re-sign fails. See ios-resign/README.md. The Mac and the server renew the same app: build-ios.sh builds under . (the ID AltServer gives a free-account app; SHARED_SIGNING=0 for the plain ID) and can copy the IPA to the server (RESIGN_HOST); the service sets the IPA's ID back to the plain one before AltServer adds the team ID. The page is published at https://iosresign.ourchirp.com through a Cloudflare Tunnel (resign-tunnel container) behind Cloudflare Access; ios-resign/cloudflare_setup.py creates the tunnel, DNS record and Access app. - Clean up deleted recordings uses the batched delete. ## 1.3.3 (chirp-device) — 2026-10-08 — ENHANCEMENT — Night callers can use the global threshold - detection.birdnet.night_global_species (set by the server's Night-Caller Thresholds, "Per species"): those night callers need min_confidence like other species, on CPU and Coral. In "Global" mode the server sends night_species_min_confidence equal to min_confidence. ## 0.5.10.l — 2026-10-06 — ENHANCEMENT — Spectrogram cache, storage and log optimizations - Display-threshold changes pre-render spectrograms for the newest 500 recordings (CHIRP_SPECTROGRAM_PREWARM) instead of every recording; each change wrote two files per recording (the Natick server had 469k spectrogram files for 24k recordings). The regen existence check used the old "_thumb" name and no palette suffix, so thumbnails were redrawn even when cached. - Storage status and Purge orphans gain a "spectrograms" target (file_cleanup.scan_spectrogram_cache): files whose recording is gone, and for recordings with audio, variants for other thresholds, palettes or pre-"_thumb2" thumbnails. Archived recordings keep everything; enhanced, bat and rumble-removed images are untouched. - GET /recordings/{id}/spectrogram: cached file name includes the palette (with a non-default palette every request regenerated the image), and the archived fallback probes likely names instead of globbing the folder. Shared spectrogram_filename() in audio_processor. - StorageManager archive, prune-by-age and prune-by-size, and Live Detect cleanup commit in batches of 20 with files removed after each commit (file_cleanup.delete_recordings); pruning also removes every spectrogram variant and the compressed twin (it only removed "{id}_spectrogram.png", a name nothing writes). Orphan scans use os.scandir instead of rglob + resolve. - Library loggers (matplotlib, httpcore, Bird Buddy, asyncio and others) set to WARNING; the root logger stays at DEBUG for Chirp's own logs. - Tests: shared fixture moved to tests/conftest.py; tests/test_storage_optimizations.py; both test files now in chirp-server-instances too. ## 0.5.10.k — 2026-10-05 — ENHANCEMENT — Recordings that show nothing are hidden - GET /recordings hides finished device recordings with no detection at the display threshold (the "Unknown" cards), so the Recordings list and Dashboard skip them. Most were server-kept owls at 0.20–0.35, which auto-purge spares. Manual uploads, bats, recordings still processing and labeled ones stay; a species or confidence filter turns the rule off. - no_detections now means "shows nothing at the display threshold" (true) or "shows at least one" (false); the filter chip is "Unknown only". ## 0.5.10.j — 2026-10-04 — CRITICAL — Deletes and uploads no longer fail during auto-purge - Recording deletes (auto-purge sweep, Purge empty recordings) run in batches of 20 with a commit each, and files are removed after the commit. The first 0.5.10.i sweep deleted 138 recordings in one transaction while globbing a 469k-file spectrograms folder per recording, holding SQLite's write lock for minutes: uploads, heartbeats and deletes failed with "database is locked" (the app showed "Load failed"). - Single and bulk delete commit first and clear files on a background thread; upload-time purges remove files after their commit; re-analysis Undo scans for spectrograms before writing. Spectrogram variants for any number of recordings come from one folder scan (app/services/file_cleanup.py). SQLite busy_timeout 15 s (was 5 s). ## 0.5.10.i — 2026-10-04 — BUGFIX — Auto-purge removes every "Unknown" recording - Auto-purge judges a recording by what it shows (detections at the display threshold) instead of the server's detection count, so night-floor Common Nighthawk and owl scores under the threshold no longer keep it; only an owl the server kept under its owl rules (0.20 plus a hoot) is spared. A sweep every 15 minutes applies it to stored device recordings (not manual uploads, labeled/noted ones or iNaturalist submissions). ## 0.5.10.h — 2026-10-04 — ENHANCEMENT — iPhone date pickers, resume handling, compressed-audio note - Date and time inputs shrink to their container and read left-aligned (iOS gave them a native minimum width that overflowed the Edit window). The iOS app uses @capacitor/app for foreground/background events (visibilitychange as fallback), reconnecting the live socket and refreshing the Dashboard on return. - BirdNET Analysis notes when a recording is stored compressed (Opus, MP3...), so re-analysis scores read as approximate. ## 0.5.10.g — 2026-10-03 — BUGFIX — Night noise no longer keeps empty recordings - A night caller keeps its recording out of the empty-recording purge (auto and manual) only when the server pass kept it, the device scored it 0.25+, or the server pass also scored it 0.10+ (near miss). Device-only owls at 0.10 had kept every night-time noise recording, since noise scores as several owls at 0.10-0.17 at once. ## 1.3.2 (chirp-device) — 2026-10-03 — BUGFIX — Coral scores like CPU; tool to check a missed call - Coral backend: sensitivity now maps the way birdnetlib's does (1.5 = slope 0.5, lifting faint calls; Coral used 1.5 as the slope, pushing a logit of -2 from 0.27 to 0.05), and it applies the night-caller floor and the location filter like the CPU backend. - tools/check-file.py runs a clip (e.g. a HaikuBox download) through the device's gate, backend and location filter and says where a call is lost; the service saves its server-applied settings to state/effective_config.json for it. tools/night-check.sh warns on devices older than 1.3.1 and shows whether the location list loaded. ## 0.5.10.f — 2026-10-02 — BUGFIX — Ollama thinking models answer - Ollama requests set num_ctx 16384 (CHIRP_OLLAMA_NUM_CTX) and, when a thinking model returns only reasoning, retry once with think: false; AI Second Opinion on Gemma 4 had failed with "consumed by thinking". The error that remains names the model and the setting. ## 0.5.10.e — 2026-10-02 — BUGFIX — AI Second Opinion no longer times out - AI Second Opinion runs in the background and the page waits for it (with elapsed time, and the error shown if the provider fails); it had been a single request that the browser, iOS app or proxy dropped after about a minute, leaving nothing on screen. Image requests get 120 s on hosted providers (was 30 s) and 420 s on Ollama (was 180 s). ## 0.5.10.d — 2026-10-02 — BUGFIX — Cleaner AI Second Opinion - Species headings show the name and likelihood once (a repeated "Blue Jay (likelihood Blue Jay (likelihood 85%)" is fixed, saved notes included); multi-species notes drop the opening summary that Overall repeated, and Overall is one or two sentences. - The "Measured check" line is now an "Audio check for " paragraph in plain sentences (faint call, no sound above the background, rumble-range energy, device-only detection, verdict lowered), saved notes included. ## 0.5.10.c — 2026-10-01 — ENHANCEMENT — Logo bars raised - The logo's three bars moved up 3 units, between the beak tip and its center line, in every theme, favicon, PWA and iOS icons, the launch screen and email header. ## 1.3.1 (chirp-device) — 2026-10-01 — BUGFIX — Owls kept by the location filter - The CPU backend applies the location filter itself, with the server's rule: species expected this week (3% or more) plus night callers expected in any week (owls 0.15%, others 0.5%). birdnetlib's filter dropped Barred Owl (1.6% in eastern Massachusetts in autumn) even when it scored 95–100%. ## 0.5.10.b — 2026-10-01 — BUGFIX — Editable recording time; downloads open a save dialog; AI Second Opinion hears chipmunks - Recording date and time can be edited (Edit on the recording page). Uploads read it from the file name (HaikuBox, AudioMoth, Song Meter, Chirp), editable per file before upload; otherwise now. - Backups, Life List exports, recording audio, logs and diagnostics open the iOS share sheet in the app (which couldn't download before) and Save As in Chrome and Edge; audio downloads keep their stored format (FLAC) and name. - AI Second Opinion gives each species a likelihood and tells repeated chips several kHz tall (Eastern Chipmunk, chip notes) from narrow insect pulses, which it had called crickets. ## 0.5.10.a — 2026-09-30 — FEATURE — Re-analysis history and Undo, owl tuning, faster analysis and statistics - Re-analysis history with Undo on each recording; Re-analyze no longer uses AI-Enhanced audio unless chosen, which then keeps the better of both scores; SSH terminal and mic levels connect again; cards and Empty Recordings count shown detections; AI Second Opinion checks recordings with none. - Owls tuned on 49 labeled clips (wrong owls 80 to 8, owl-free false alarms 7 to 1); night-caller near misses with per-species thresholds; BirdNET about 2x faster, Statistics and Life List 13–23x, first load a third the size. ## 0.5.9.h — 2026-09-30 — ENHANCEMENT — Re-analysis change log; FLAC by default - Re-analysis logs each species it removes and why (location filter, score now vs threshold, no score), plus any kept below the display threshold; see Logs. - New installs compress recordings to FLAC (lossless), so re-analysis reads the same audio as the upload pass. ## 0.5.9.g — 2026-09-30 — BUGFIX — No zoom when typing on iPhone - iOS app and Safari no longer zoom in when a text field is focused (viewport maximum-scale 1); the app shell had no way to zoom back out. - Keyboard shortcut hints (⌘↵ to post) hidden on touch screens. ## 0.5.9.f — 2026-09-30 — BUGFIX — Re-analysis keeps detections found on the original audio - Re-analysis of compressed (Opus) or AI-Enhanced audio keeps earlier server detections, at their earlier score, when the new pass scores them within 0.15; scores shifted by compression had been dropping them below the threshold or the 0.15 display floor. - Re-analysis applies server_reanalyze_delete_device like uploads, honors the min_conf parameter, and uses the 2.5 s overlap default. ## 0.5.9.e — 2026-09-30 — ENHANCEMENT — iOS app polish - iOS app: launch screen hides as soon as Chirp renders; status bar follows light and dark themes; offline page with retry; service worker enabled (app-bound domains) for faster launches. - iOS app: runs on iOS 27 (UIScene lifecycle; built with Xcode 27 it closed at launch); reloads after a server update; bottom-bar haptics; install steps hidden in Settings and Tutorial. ## 0.5.9.d — 2026-09-30 — BUGFIX — Owls found in noisy night recordings - Location filter no longer drops owls and other night callers (eBird rarely logs them); their best week of the year decides. - Owls are scored on a 200–2000 Hz copy and need a matching hoot, so insect choruses no longer hide owls or pass as Eastern Screech-Owl. ## 0.5.9.c — 2026-09-29 — BUGFIX — iOS app asks for microphone and notifications - iOS build adds NSMicrophoneUsageDescription (the mic was denied without a prompt); the app tags its user agent ChirpApp/1. - One-time setup card in the iOS app requests notifications and the microphone; launch no longer prompts silently; Live Detect points to Settings → Chirp. ## 0.5.9.b — 2026-09-29 — BUGFIX — iOS app spacing at top and bottom - iOS app web view runs edge to edge (contentInset never), so safe-area spacing applies once; Liquid Glass top clearance limited to Safari home-screen apps (navigator.standalone). ## 0.5.9.a — 2026-09-29 — FEATURE — Refined Songbird logo and motion - Logo: closed lifted beak, one-line body, warm bars rising with the beak, size-adaptive line weight; all 17 themes, favicon, PWA, iOS and email icons. - Motion: Sing on the Dashboard, Draw-on at login and loading, Live meter while Live Detect listens; still elsewhere and under Reduce Motion. ## 0.5.8.e — 2026-09-29 — ENHANCEMENT — iOS app loads ourchirp.com; iPhone layout fixes - iOS app's server URL is now https://ourchirp.com (www allowed); example addresses in Settings updated to match. - Page no longer drags or bounces behind the app shell (web and iOS app); home-screen top bar clears iOS 26's Liquid Glass blur band. ## 0.5.8.d — 2026-09-29 — ENHANCEMENT — Icons refresh after updates, iOS re-add prompt and build - Icon URLs carry ?v=VERSION, the manifest gets a fixed id, icons are sent no-cache; old iOS installs get a one-time re-add card. - iOS build: single 1024 px icon, new launch screen, Bluetooth text set automatically, `--archive` for a signed archive. ## 0.5.8.c — 2026-09-29 — ENHANCEMENT — New Songbird logo across every theme - New logo: outline songbird with an open beak and meter bars; sings on the Dashboard, still elsewhere, respects Reduce Motion. - All 17 themed logos, favicon, PWA, iOS and email icons redrawn from one shared SongbirdMark component. ## 0.5.8.b — 2026-09-28 — ENHANCEMENT — AI Second Opinion hears barks and high buzz; solid iOS top bar - AI Second Opinion measures broadband bursts (dog barks) and steady high-frequency bands and reports them when the model omits them; Experimental tag removed. - Top bar is sticky and theme-color follows the theme so iOS Liquid Glass tints it instead of blurring. ## 0.5.8.a — 2026-09-28 — FEATURE — AI Second Opinion measured checks, steadier Tailscale, Rumble Auto Off fix - AI Second Opinion gets measured band, loudness, server agreement and regional frequency per species; unsupported Consistent verdicts become Uncertain; faster on local models. - Server Tailscale recovers from an orphaned daemon and Auto uses it; Listen and mic levels fall back to the other address; Rumble Removed respects Auto Off. ## 1.3.0 (chirp-device) — 2026-09-28 — FEATURE — XVF3800 raw mics, night tuning, one-line logs - Bundles 1.2.30–1.2.31 (XVF3800 routing, window stats, night-caller floor, 2 BirdNET threads) plus single-line journal logs and a clearer Listen error when the bat detector isn't capturing. ## 0.5.7.ah — 2026-09-25 — BUGFIX — Server confirms weak device owl detections - A device night-caller detection under 0.10 now needs the server pass to confirm it; otherwise it's dropped and the recording can be auto-purged. ## 0.5.7.ag — 2026-09-25 — FEATURE — Night callers kept through re-analysis and auto-purge, connect to devices over Tailscale - Owls, nightjars, rails and woodcock need 0.10 in the server and device BirdNET passes and survive re-analysis and auto-purge; device gate default is now -90 dB. - Devices page offers Coral only when a compiled model is on the device; night-report.py matches whole species names and drops the fixed Haikubox confidence. - Server Connects Via on each device (Auto, LAN IP, Tailscale): SSH, logs, Listen and mic levels use the Tailscale IP when the server is on the tailnet. ## 1.2.31 (chirp-device) — 2026-09-25 — BUGFIX — steadier overnight CPU detection, night-check fixes - night-check.sh finds its install dir (~/chirp-device), parses "yesterday 19:00" for journalctl, lists mics, and flags a volatile journal; install.sh makes the journal persistent. - -90 dB gate, 0.10 floor for night callers, 2 BirdNET threads; a backlog cuts overlap to 1 s instead of dropping windows; CPU is the default backend. ## 0.5.7.af — 2026-09-25 — FEATURE — Coral nudge, XVF3800 raw-mic switch, Enhanced cache cleanup, night report - Devices page flags an unused or failed Coral with a one-click switch, and adds a ReSpeaker XVF3800 Raw/Processed output toggle. - Deleting or archiving a recording removes its Enhanced caches; Storage cleans up orphaned and old-version ones. New `scripts/night-report.py` compares overnight Chirp and Haikubox detections. ## 1.2.30 (chirp-device) — 2026-09-25 — FEATURE — XVF3800 raw capsules, effective backend, gate stats - XVF3800 routes raw capsules instead of the voice-processed beam (`respeaker.xvf3800_output`, default raw, hot-applied); heartbeat reports the backend actually running. - 10-minute "Window stats" log line (levels vs gate, drops) and `tools/night-check.sh` for overnight checks. ## 0.5.7.ae — 2026-09-25 — BUGFIX — Night owls reach BirdNET again, better AI Second Opinion and Enhance - Server-pushed device defaults (-55 dB gate, 0 dB gain) blocked distant owls; now -85 dB / +6 dB, auto-upgraded on heartbeat, and settings sync keeps per-device tuning. - AI Second Opinion: mel-scaled labeled image, measured species bands, 60 s crop on long recordings. Enhanced/AI Enhance notch steady insect tones; owls and doves get a 150 Hz rumble cutoff. ## 1.2.29 (chirp-device) — 2026-09-25 — BUGFIX — owl-friendly energy gate - Energy gate checks the loudest 0.5 s of each window (not the last), 150 Hz low cut, hot-applied; BirdNET sensitivity on CPU and Coral; dropped windows logged. ## 0.5.7.ad — 2026-09-24 — BUGFIX — AI Second Opinion now reports insect chirps it previously missed - A cricket-like chirp repeating about once a second at ~5 kHz was visible on the spectrogram but reported as "no non-avian sounds." The audio is now measured for steady repeating tones, the result is given to the model, and it's added to Also visible if the model leaves it out. Detection boxes in the image sent to the model are outlines only. ## 0.5.7.ac — 2026-09-22 — FEATURE — AI-Enhanced re-analysis, recording SNR, split-band AI Enhance - Re-analyze can now run BirdNET on AI-Enhanced audio. Enhancement was previously playback-only and never affected detections. - Each recording gets an SNR estimate. Noisy recordings show a notice and default the AI-Enhanced re-analysis option on. - AI Enhance now keeps calls above 7 kHz instead of cutting them off, and skips that when the high band is only noise. Compression settings note that Opus loses high-frequency detail used by re-analysis. ## 0.5.7.ab — 2026-09-22 — BUGFIX — removed the misleading confidence badge on Haikubox detections - Haikubox's API doesn't return a real confidence score, so every detection showed a hardcoded 90%. Confirmed no way to get it directly or from Haikubox audio (the API has neither). Removed the badge. ## 0.5.7.aa — 2026-09-22 — BUGFIX — AI Second Opinion no longer shows empty, duplicated section headers - The cleanup that strips a truncated trailing section only checked the last one, missing a case where empty headers stack up before a section that does have content. Now checks every section and drops the empty ones, wherever they fall. ## 0.5.7.v — 2026-09-21 — FEATURE — AI Second Opinion no longer truncates on long recordings (real fix + continuation-call fallback), Rumble Removed gets a third "Enabled (off)" mode - Reported directly with a real example (a 296s, 4-species recording, still cutting off mid-sentence even after the token floor was already raised once): Ollama's num_predict is now set to -1 (its own documented "infinite generation" value) instead of any fixed cap, since a fixed cap just picks a recording length that will eventually fail again. As defense-in-depth for whatever the model's own context window still can't fit, a genuinely truncated-looking response now triggers up to 2 automatic continuation calls in the same conversation, concatenated — implementing the exact fallback suggested. - Rumble Removed's setting gains a third mode, "Enabled (off)" — the toggle is shown on every recording (same as "Enabled (on)") but starts off by default, for admins who want people to be able to turn it on themselves without it being applied automatically. Replaces the old hidden "Always on" mode, which didn't cover this case at all. ## 0.5.7.t — 2026-09-20 — FEATURE — Headscale node list in Global Admin (instances), chirp-multi fully removed, container architecture documented, Tutorial/tech docs updated for AI Enhance/AI Second Opinion/Rumble Removed - Global Admin's Network tab (instances-only) now shows every device connected to the bundled Headscale server — online status, IP, last seen — via a new loopback-only Headscale admin API (auto-provisioned key, zero manual setup). Needs a live smoke-test against a real instance. chirp-multi (the deprecated earlier multi-tenant attempt) is fully removed — the directory, its real upload protocol in chirp-device's uploader.py, and every reference across docs — including a genuinely separate pre-existing test bug found and fixed along the way. - Documented the container architecture (Headscale/Caddy/cloudflare-ddns for instances, the launcher for chirp-server) in tech docs, both READMEs, and ARCHITECTURE.md. Tutorial now explains Rumble Removed, AI Enhance, and AI Second Opinion; tech docs and the top-level README now connect "Call Re-analysis" to the AI Second Opinion button users actually see, and document the audio-cleanup toggles that weren't in the README before. ## 0.5.7.s — 2026-09-20 — BUGFIX — AI Second Opinion could end in a dangling, empty section header on Ollama — real token-budget fix plus a defensive display cleanup - Reported directly with a screenshot: a response ending in a bare "**Other species detected:**Also visible:" with nothing after either. Root cause is documented in ai_providers.py's own comments — Ollama's thinking-capable local models spend part of their token budget on hidden reasoning before the visible answer, and the multi-species prompt has grown substantially (a named per-species checklist, a self-check instruction, two extra sections) since the 2048 floor was set. Raised to 3072. Also added a defensive cleanup that strips a trailing section whose heading has essentially no content after it, so any future truncation shows a clean, complete-looking response instead of a broken dangling header — verified against the exact real truncation pattern reported, and confirmed a genuinely complete section is left untouched. ## 0.5.7.r — 2026-09-19 — BUGFIX — found the actual root cause of AI Second Opinion discussing species that were never shown: no confidence filter at all - Reported directly, more than once, with the correct hypothesis attached: AI Second Opinion's detection query pulled every Detection row for a recording with zero confidence filtering, while the spectrogram image, the species-count badge, and the detections list all filter by min_confidence_display (the same threshold recordings.py's own detail endpoint uses). A detection sitting below that threshold is real data the user never actually saw as a flagged detection — but the prompt was treating it exactly like one of BirdNET's own flagged species, in the same paragraph, with no distinction. Filtered by the same threshold now, so what AI discusses as "flagged" matches what was actually shown. ## 0.5.7.q — 2026-09-19 — BUGFIX — found and fixed the Settings gap above Diagnostics, open since early this session - Every other Settings tab's anchor is its own GroupHeader doing double duty as both the tab-switcher anchor and the visible heading (one element, pt-2). Diagnostics used a separate, invisible pt-4 div as its anchor, sitting before its own custom heading button which had no top padding of its own — an extra 16px on top of what every other tab gets, from an element that isn't part of the heading at all. Anchor moved directly onto the button, padding matched to every other tab's pt-2, extra div removed. ## 0.5.7.p — 2026-09-19 — BUGFIX — restored the changelog body text stripped from every 0.5.6/0.5.7 entry last round - Condensing the changelog last round dropped every entry's body down to nothing — reported directly: expanding an entry revealed a genuinely empty card, not a shorter one. Restored substantive detail for all 46 affected entries. ## 0.5.7.o — 2026-09-19 — BUGFIX — Changelog page was hiding entries from a fresh minor version's root release, and AI Second Opinion's species boundary made more concrete - A minor version's root release (e.g. "0.5.7", the AI Enhance debut — no letter suffix, unlike every patch after it) was being grouped by the same MAJOR.MINOR rule meant for chirp-device's numbering scheme, landing it in a stray "0.5" group separate from its own 0.5.7.a–0.5.7.n siblings — looked like the card was missing an entry, since the count didn't match what was shown. Fixed to group correctly with its siblings. - AI Second Opinion's per-species boundary (added last round) reportedly still blended a non-flagged species into the main verdict section — strengthened by naming the exact species list directly in the instruction and adding an explicit self-check before writing, rather than only referencing the list abstractly. ## 0.5.7.n — 2026-09-18 — FEATURE — mobile player layout fixed, AI Second Opinion now clearly separates BirdNET-confirmed vs. AI-only species, and a Dashboard cache staleness gap closed - The play controls row crammed the play button, timer, and up to four toggles onto one line, pushing the last one off-screen on a phone. Split into two rows — play + time, then toggles wrapped below. AI Second Opinion's "Also visible" pass now separately labels **Other species detected:** (a bird call BirdNET never flagged, with AI's own species guess) from **Also visible:** (non-bird sounds) — previously blended into one paragraph. Also closed a Dashboard cache gap: statistics_cache was only invalidated on settings changes, never on a new detection, so a species detected for the first time today could show as not-new for up to 45 seconds. ## 0.5.7.m — 2026-09-18 — BUGFIX — the real reason rebuilding never showed the new tech-doc figures: app/techdocs_content/ was never re-synced - The in-app Tech Docs page doesn't read techdocs/ directly — the Dockerfile only COPYs app/, so techdocs/ sits outside the build context entirely, and scripts/sync-techdocs.py has to copy techdocs/docs/ into app/techdocs_content/ before every build. That sync was never run after the figure rebuild, so every package shipped the old figures baked into app/techdocs_content/assets/ regardless of version — no amount of rebuilding could have picked up the new ones until this ran. ## 0.5.7.l — 2026-09-18 — DOCS — all 10 tech-doc figures rebuilt from scratch: dark theme, no overlapping text, patent framing stripped - Two figures (System Architecture, Detection Pipeline) were still the original white-background patent-style versions with "FIG. N" labels and reference numerals; several others (Dual-Path Audio, Enrichment Pipeline, Hardware) had genuine overlapping-box and clipped-text bugs. Built a reusable auto-sizing SVG generator (boxes measured from actual text, so nothing can overlap by construction) and rebuilt all 10: same information, consistent dark theme, all patent-litigation framing ("NOVEL OVER PRIOR ART", "Claim 1", reference numerals) removed. ## 0.5.7.k — 2026-09-18 — DOCS — tech docs updated for this session's changes; fig2 and several stale numbers corrected - Fig2's threshold (−55 dBFS) and RMS window (50ms) were stale even before this session — the real values have been −70dB/500ms for a while. Corrected those plus this session's own changes (−85dBFS, 0.05 confidence), removed an unverified "~80% discard" stat with no real data behind it, and added a previously-missing section documenting the three server-side audio processing options (Enhanced, Rumble Removed, AI Enhance) — none of which were covered anywhere in the site before. ## 0.5.7.j — 2026-09-18 — FEATURE — Rumble removed now has a manual cutoff slider, matching Live Detect's own rumble filter - The cutoff was always the automatic species-aware value (250Hz for owls, 800Hz otherwise) with no way to adjust it directly. Added a 0-2000Hz slider (same range as Live Detect's own rumble filter) that appears when Rumble removed is on, starting at Auto. Verified end-to-end: a 200Hz cutoff kills a 100Hz tone but preserves 400Hz/1000Hz; 600Hz kills both 100Hz and 400Hz while preserving 1000Hz. ## 1.2.28 (chirp-device) — 2026-09-18 — TWEAK — detection sensitivity raised (energy threshold, confidence, window overlap) ## 0.5.7.i — 2026-09-18 — TWEAK — detection sensitivity raised across both device and server pipelines - Per a direct comparison against HaikuBox catching more activity at the same location: lowered birdnet_min_confidence (10%→5%), raised birdnet_sensitivity to its max (1.0→1.5), and raised birdnet_overlap (1.5→2.5) server-side. Matched on the device's own local fallback config: energy_filter.threshold_db −70→−85dB, birdnet.min_confidence 0.25→0.05, overlap_seconds 0→2.0 (previously no window overlap at all). ## 0.5.7.h — 2026-09-18 — BUGFIX — Rumble removed's label never changed with its actual state, and the spectrogram never reflected it either - The full-recording "Rumble removed" button always displayed that exact text regardless of whether it was actually on — only the border/background color correctly tracked state, the label text was static. Fixed to match the clip-level button's own dynamic label. Also added the missing /spectrogram/rumble-removed endpoint — toggling the audio never updated the spectrogram image, the same gap Enhanced and AI Enhanced already had fixed for their own toggles. ## 0.5.7.g — 2026-09-18 — FEATURE — rumble removal now available per-clip, and AI Second Opinion more reliably flags a constant background chorus (cicadas, crickets) - Rumble removal never existed at the clip level — only the full-recording player had it, so checking via an individual clip had nothing to toggle. Added a matching per-clip endpoint and button. Also strengthened AI Second Opinion's "Also visible" instruction to explicitly cover a continuous band running across the whole spectrogram (a nonstop cicada/cricket chorus), not just a one-off, localized noise event — the earlier wording read as expecting something more discrete. ## 1.2.27 (chirp-device) — 2026-09-18 — BUGFIX — input_gain_db restored to 6.0 — the 0.0 revert was silently dropping real bird calls ## 1.2.26 (chirp-device) — 2026-09-18 — TWEAK — server_sync.targets: swapped, chirp-server-instances is now primary ## 0.5.7.f — 2026-09-18 — FEATURE — AI Enhance now strips rumble too, using the same simple high-pass as Rumble Removed - AI Enhance had no rumble-specific filtering of its own — it relied entirely on the neural model, which doesn't specifically target low-frequency content. Added the same species-aware high-pass filter the classical Rumble Removed toggle uses, as a pre-processing step before the model runs. Measured directly: a 60/120Hz rumble band dropped from 2320 to 8.4 RMS (99.6%) while the call band was preserved and boosted. ## 1.2.25 (chirp-device) — 2026-09-18 — BUGFIX — "Applied server config" logged before the callback ran, and multi-target logs didn't say which server ## 0.5.7.e — 2026-09-17 — BUGFIX — Rumble removal fixes weren't taking effect: three cache paths never matched what actually got written - enhance_audio() always appends "_enhanced.wav" to its cache filename, but three endpoints' own cache-existence checks didn't match that — two were silently recomputing on every request instead of ever hitting the cache, and rumble-removed's real output was colliding with rumble-removed-enhanced's old cache path. A clip Enhanced even once before the underlying filter fix also kept serving stale audio indefinitely, since it had no version tag. All three corrected with a shared DSP_VERSION constant. ## 0.5.7.d — 2026-09-17 — BUGFIX — Changelog page: several device entries were showing under the Server tab, and Device showed empty - Several recent chirp-device entries were missing the internal "(chirp-device)" tag the page needs to sort them correctly, so they were appearing under the Server tab instead and Device looked empty. ## 1.2.24 (chirp-device) — 2026-09-17 — TWEAK — input_gain_db reverted from 6.0 back to 0.0 ## 0.5.7.c — 2026-09-17 — FEATURE — AI Enhance now updates the spectrogram, shows a loading state, and torch/torchaudio install by default - The AI-Enhanced spectrogram wasn't being generated at all — toggling AI Enhance changed the audio but the image stayed on the original. Added the missing endpoint, plus a loading spinner while the neural pass runs (0.2-0.3x realtime on CPU, so genuinely takes a few seconds). torch/torchaudio now install by default via the CPU wheel index, confirmed via a full dry-run with no dependency conflicts. ## 0.5.7.b — 2026-09-17 — BUGFIX — Rumble removed still sounded noisy after the filter fix — root cause was the loudness boost afterward, not the filter - Rumble Removed's own noise-reduction boost step was re-amplifying the noise it had just filtered out — soft_limit_and_normalize() was unconditionally boosting to a fixed peak after filtering, and since rumble often dominates a signal's peak, removing it then boosting made the remaining hiss roughly 4x louder. Fixed via a boost parameter: rumble-only passes now skip the boost entirely, matching Enhanced's own already-correct behavior. ## 0.5.7.a — 2026-09-17 — FEATURE — Automatic backups now show real last-run status, plus a "Run Now" button - Automatic backups previously had no visible status at all — no way to tell if the last scheduled run succeeded, failed, or ran on time. Added real last-run/last-success timestamps and the last error (if any) to Settings, plus a "Run Now" button to trigger one manually and see the result immediately rather than waiting for the next scheduled window. ## 0.5.7 — 2026-09-17 — FEATURE — AI Enhance (Experimental): neural-network denoising, separate from Enhanced/Rumble removed - First iteration of AI Enhance (Experimental) — neural-network denoising using biodenoising (Earth Species Project, Demucs-based, trained on animal vocalizations), a full alternative to the classical Enhanced/Rumble Removed pipeline rather than a variant of it. Vendored the minimal inference code so the pickled checkpoint's module path still resolves correctly. Measured on a synthetic 48kHz test signal: background-region peak dropped from 0.089 to 0.011 while the call itself was preserved and boosted. Known limitation, documented in the UI: the model runs at 16kHz internally, so content above 8kHz in the result is lost. ## 0.5.6.z — 2026-09-17 — BUGFIX — Rumble removed was actually a bandpass, not a high-pass, and rang audibly at the band edges - Fixed several issues found from real reports on the mic testing and bat features shipped just before this: AudioMoth's own mic test, the detection bar rendering on bat recordings, and a horizontal-scroll overflow on mobile. ## 1.2.23 (chirp-device) — 2026-09-17 — BUGFIX — A config version could be marked "applied" during startup without ever actually reaching anything live ## 0.5.6.y — 2026-09-16 — TWEAK — Rumble removed now defaults to on for bird recordings - Rumble Removed was opt-in, requiring a toggle on every recording — made it default to on for bird recordings, per direct feedback that most people want it on by default. Still a real toggle, not forced; switch it off to hear the unfiltered original. ## 0.5.6.x — 2026-09-16 — FEATURE — AI Second Opinion now notes other visible sounds; per-mic on/off toggle added - AI Second Opinion can now flag other sounds visible in a spectrogram beyond the flagged detection (an early version of the "Also visible" instruction that was substantially strengthened later this session). Also added a per-mic on/off toggle for detection, so a specific microphone can be excluded without physically unplugging it. ## 0.5.6.w — 2026-09-16 — FEATURE — Rumble removed is now its own separate toggle from Enhanced - "Rumble removed" is now its own separate toggle from Enhanced rather than bundled into it — independently combinable, matching how De-wobbled and Enhanced already combine for bat recordings. ## 1.2.22 (chirp-device) — 2026-09-16 — TWEAK — input_gain_db default raised from 0.0 to 6.0 ## 0.5.6.v — 2026-09-16 — FEATURE — recordings now get a species-aware high-pass filter when Enhanced - Recordings now get a species-aware high-pass filter applied when Enhanced is on — the foundational version of what later became the standalone Rumble Removed toggle. Cutoff varies by species (lower for owls and other low-frequency callers) so the filter doesn't cut into the bird's own call along with the rumble. ## 1.2.21 (chirp-device) — 2026-09-16 — BUGFIX — "Config pending" could resurface after being dismissed even when the device had actually applied it ## 1.2.20 (chirp-device) — 2026-09-16 — BUGFIX — Mic Frequency Response's AudioMoth lookup 404'd despite matching elsewhere ## 0.5.6.u — 2026-09-16 — BUGFIX — several fixes from real reports on the mic testing/bat features just shipped - Fixed several issues found from real reports on the mic testing and bat features just shipped — AudioMoth's mic test, the detection bar not rendering correctly on bat recordings, and a horizontal scroll issue on mobile. ## 0.5.6.t — 2026-09-16 — FEATURE — mic testing added to Devices page and Diagnostics; several fixes - Added mic testing to the Devices page and as its own entry in Diagnostics, fixed the spectrogram not updating after a manual reanalyze, and fixed bat clip playback. ## 0.5.6.s — 2026-09-16 — BUGFIX — Safari upload failure persisted after the XHR fix - The Safari upload failure persisted even after switching the page's own upload code from XMLHttpRequest to fetch() — the service worker itself was also re-forwarding upload requests in a way that could drop the body specifically on Safari, a separate WebKit bug one layer further out from the first one. ## 0.5.6.r — 2026-09-16 — BUGFIX — recording and photo uploads silently failed on Safari - Recording and photo uploads were silently failing in Safari — a real WebKit bug where Safari can send an empty upload body while everything otherwise looks normal. Worked fine in Chrome on iOS and Safari on macOS the whole time, which is what made it hard to notice. ## 1.2.19 (chirp-device) — 2026-09-14 — TWEAK — Stage 1 bandpass lowered from 800 Hz to 250 Hz ## 0.5.6.q — 2026-09-14 — DIAGNOSTIC — logs the real headers behind every 422 validation error - Added a diagnostic log for every request validation failure (422), showing the real Content-Type and Content-Length the server actually received — helps pin down upload failures that don't reach application code at all. ## 0.5.6.p — 2026-09-14 — BUGFIX — upload errors showed "[object Object]" instead of the real message - Fixed upload errors showing the literal text "[object Object]" instead of the actual reason — Manual Recording Upload, bird photos, Live Detect, and Mic Frequency Response all now surface a real, readable error message. ## 0.5.6.o — 2026-09-14 — TWEAK — replaced confusing seasonal percentages with a plain bar chart - Replaced confusing seasonal percentages with a plain bar chart, matching eBird's own weekly bar chart style — shown on bird profiles and in the Field Guide. ## 0.5.6.n — 2026-09-14 — TWEAK — bird profile summary auto-refresh cap raised from 25/day to 100 - Raised the bird profile summary auto-refresh daily cap from 25 to 100, so a real backlog of species clears in a day or two instead of spreading out across a week. ## 0.5.6.m — 2026-09-14 — BUGFIX — Bat profile photo permanently stuck empty - Fixed the Bat profile photo staying empty forever — one failed fetch (iNaturalist rate limiting) was marked "done" and never retried again, even though a later attempt would likely have succeeded. ## 0.5.6.l — 2026-09-14 — BUGFIX — Tech Docs images cached for a week, hiding updates after a redeploy - Fixed Tech Docs images being cached for a full week, hiding updated figures for that long after any redeploy — the direct precursor to the same-shaped bug found again later this session, just with a shorter 5-minute window this time instead of none at all. ## 0.5.6.k — 2026-09-14 — BUGFIX — several small fixes - Several small fixes found while auditing related code. ## 0.5.6.j — 2026-09-14 — BUGFIX — silent failure backfilling a missing iNaturalist taxon ID - Fixed a silent-failure gap when backfilling a missing iNaturalist taxon ID during a maps-only refresh — now logs whether it actually succeeded or genuinely found nothing, instead of leaving both cases indistinguishable. ## 0.5.6.i — 2026-09-14 — BUGFIX — Macaulay range-map fallback disabled; Changelog cleanup - Disabled the dead Macaulay Library range-map fallback (no longer a working source) and cleaned up an ambiguous count shown on the Changelog tabs. ## 1.2.18 (chirp-device) — 2026-09-14 — BUGFIX — Mic Frequency Response's AudioMoth stream now matches by its real device name ## 0.5.6.h — 2026-09-10 — BUGFIX — several small fixes - Several small fixes found while auditing related code. ## 0.5.6.g — 2026-09-10 — BUGFIX — several small fixes - Several small fixes found while auditing related code. ## 0.5.6.f — 2026-09-10 — FEATURE — Changelog split into Server/Device tabs; Mic Frequency Response shows a spectrogram - Changelog page split into separate Server and Device tabs — previously a single interleaved timeline made it harder to follow either project's own release history. Mic Frequency Response diagnostic now also shows a spectrogram, not just the raw frequency-response numbers. ## 0.5.6.e — 2026-09-10 — BUGFIX — Changelog mis-grouped every chirp-device entry into its own card - Fixed the in-app Changelog page mis-grouping every chirp-device release into its own individual card instead of grouping same-minor-version releases together, the direct predecessor of the grouping bug found again later this session for a different edge case (a minor version's bare root release). ## 0.5.6.d — 2026-09-10 — FEATURE — a way to dismiss a "Config pending" state that isn't really pending - Added a way to dismiss a "Config pending" state on the Devices page for a device that isn't actually pending anything — the state could get stuck showing pending indefinitely in some cases. ## 0.5.6.c — 2026-09-10 — FEATURE — overlapping Live Detect windows with a length slider; new Mic Frequency Response diagnostic - Live Detect's listening window is now longer and overlapping, with an adjustable length slider — previously a fixed, shorter, non-overlapping window could miss the start of a call landing right at a window boundary. New Mic Frequency Response diagnostic tool added for comparing microphones side by side. ## 0.5.6.b — 2026-09-10 — BUGFIX + FEATURE — Live Detect had a capture gap; added a sensitivity slider - Live Detect had a capture gap between consecutive analysis windows that could miss a brief call landing in that gap — fixed, plus added a new sensitivity slider. ## 1.2.17 (chirp-device) — 2026-09-10 — BUGFIX — bat detections only reached the first upload target ## 1.2.16 (chirp-device) — 2026-09-10 — BUGFIX — previous fix's own bug: only tried one of two needed dev packages ## 1.2.15 (chirp-device) — 2026-09-10 — BUGFIX — install.sh was missing a build dependency for PyGObject ## 1.2.14 (chirp-device) — 2026-09-10 — CONFIG — Tailscale login_server switched to the Cloudflare-tunneled Headscale URL ## 1.2.13 (chirp-device) — 2026-09-10 — BUGFIX — Tailscale silently failed: "Access denied: checkprefs" ## 1.2.12 (chirp-device) — 2026-09-10 — CONFIG — real Tailscale auth key saved in config.yaml ## 1.2.11 (chirp-device) — 2026-09-10 — CONFIG — pointed at the bundled Headscale server ## 1.2.10 (chirp-device) — 2026-09-10 — CONFIG — activated multi-target server_sync, unified device key ## 1.2.7 (chirp-device) — 2026-09-09 — BUGFIX — Live Detect's device mode 401'd against any server but the first ## 1.2.6 (chirp-device) — 2026-09-09 — FEATURE — server_sync now supports multiple targets, matching upload ## 1.2.5 (chirp-device) — 2026-09-09 — BUGFIX — a secondary upload target never paired ## 0.5.6.a — 2026-09-10 — CONFIG — version numbers reconciled with chirp-server-instances - chirp-server and chirp-server-instances version numbers reconciled — both projects now share the same version number going forward instead of drifting independently. ## 0.5.5.ad — 2026-09-10 — BUGFIX — Devices page header overlapping on mobile - Reported directly, from a live screenshot: the "Config pending" badge, SSH button, Tailscale badge, logs button, and expand chevron no longer fit in one row on a narrow phone screen — five items now, up from two or three when this layout was first built — and overlapped the device's own status line ("Online · 5m ago · last recording...") above it instead of wrapping. Fixed: that whole action row now drops to its own line below the name/status on narrow screens (unchanged on desktop, where it still sits inline), and both it and the status line wrap internally if still crowded, instead of clipping into each other. ## 0.5.5.ac — 2026-09-10 — DOCS — new Tech Docs page for Tailscale/Headscale, mac/iOS toggling, and troubleshooting - Requested directly: a new "Remote Access (Tailscale/Headscale)" page in the in-app Tech Docs, covering the bundled Headscale/Caddy/DDNS setup, macOS/iOS enable-disable instructions, and every root cause found and fixed while getting this working live — daemon permission errors, missing kernel TUN modules, Cloudflare's protocol incompatibility, and the IPv6 DERP-bootstrap timeout. Also corrected docs/REMOTE_DEVICES.md, which described an earlier, never-built design (a separate docker-compose.headscale.yml, a self-hosted DERP relay, chirp-multi) rather than what shipped in chirp-server-instances, and added a caveat to docs/CLOUDFLARE_TUNNEL.md that Cloudflare — Tunnel or regular proxying alike — cannot be used in front of Headscale at all. ## 0.5.5.ab — 2026-09-10 — DEBUG — Tailscale's userspace-networking fallback timing out with no visibility - Reported from live logs: the kernel-TUN → userspace-networking fallback (0.5.5.aa) worked correctly — Unraid's kernel doesn't even have the TUN module loaded, confirming userspace mode is the only viable path on this host, not just a workaround. But that daemon doesn't crash, it stays running and tailscale up just times out after 60s without completing — and since the existing crash-detection logging only fires when the daemon actually exits, this left zero visibility into what it's stuck on (DERP, the auth key, a network path specific to userspace mode). Now surfaces that daemon's own ongoing log on a timeout too, not just on a crash — verified directly that the log content reaches the warning output in this exact scenario. ## 0.5.5.aa — 2026-09-10 — BUGFIX — Tailscale now automatically recovers from a kernel TUN permission failure - Reported from live logs: "tstun.New(\"tailscale0\"): operation not permitted" — the container couldn't create the kernel network interface even with NET_ADMIN and /dev/net/tun granted in docker-compose.yml (several possible causes: a container that needs a true recreate not just a rebuild, a host-level restriction — no way to tell which from here). Rather than require debugging Docker/host capability plumbing, TailscaleManager now catches this exact failure and automatically retries once with userspace networking, which needs no special kernel permissions at all. Verified with a test reproducing the precise error message from the logs: confirms the fallback triggers and the connection completes. ## 0.5.5.z — 2026-09-10 — DEBUG — Tailscale daemon crash reason was completely invisible - Reported from live logs: tailscaled kept getting relaunched every ~5s ("Started tailscaled") but tailscale up kept failing with "doesn't appear to be running" — meaning the daemon was dying immediately every time, and its own stderr was being thrown away (redirected to DEVNULL), so there was no way to see why. Two fixes: (1) the daemon's own process is now tracked, so a second launch is skipped while the first is still alive instead of spawning duplicates that fight over the same TUN device; (2) its stdout/stderr now go to a log file, and that log's tail is surfaced directly in this app's own warning log the moment a connection attempt fails — no more guessing blind. Verified against a mocked tailscaled that crashes immediately on a simulated permission error: confirms the crash reason now appears in our own logs. ## 0.5.5.y — 2026-09-10 — BUGFIX — server-side Tailscale still stuck on "enabled, not connected" — root cause found from the logs - Confirmed from a live log: PermissionError: [Errno 13] Permission denied: '/var/run/tailscale'. This container drops from root to an unprivileged user via gosu (see docker-entrypoint.sh) before TailscaleManager ever runs, so it could never create directories under /var/run or /var/lib — the previous fix (starting tailscaled itself) was correct, but the paths it tried to use required root either way. Moved the daemon's state and socket under /app/data instead — the same directory this app already writes recordings and its database to as that same unprivileged user, so no elevated permissions are needed anywhere. The docker-compose.yml volume for the old /var/lib/tailscale path is no longer needed and has been removed. Verified directly: a test that makes /var/run and /var/lib/tailscale forbidden confirms the code never touches either anymore, and still connects successfully. ## 0.5.5.x — 2026-09-10 — CLEANUP — parity audit against chirp-server-instances, plus a lint pass - Full audit requested directly: diffed every shared source file between this project and chirp-server-instances looking for genuine drift, not just architectural differences. Found and fixed real gaps on the instances side (push-notification daily digests were completely missing there, and its digest scheduler had the same "operates on the wrong database" bug already fixed elsewhere this session) — see that project's own changelog for the full list; nothing needed fixing here as a result of that audit. - Separately, ran pyflakes over the whole app/ tree: removed one confirmed-dead import (an unused sqlalchemy.text alias in diagnostics.py). A couple of harmless unused-local-variable warnings remain (diagnostics.py, devices.py) — flagged, not touched, since fixing them meant tracing surrounding logic for zero functional benefit. ## 0.5.5.w — 2026-09-10 — BUGFIX — Devices page never showed the Tailscale badge, even when connected - Confirmed directly, by querying the database: a device's Tailscale IP was being received and stored correctly the whole time — the DeviceOut API response model simply never declared a tailscale_ip field at all, so Pydantic silently stripped it out of every device response before it ever reached the frontend. Added. The Devices page's Tailscale badge and the SSH terminal's Local/Tailscale toggle should now appear correctly for any already-connected device with no config or restart needed on the device's own end. ## 0.5.5.v — 2026-09-10 — BUGFIX — server-side Tailscale showed "enabled" forever, never actually connected - Root cause: `tailscale up` only talks to an already-running `tailscaled` background daemon. On a normal Linux host, installing the tailscale package auto-starts that daemon via systemd — this container has no systemd at all (no init system, PID 1 is this Python process), so the daemon was installed but never actually running; every connection attempt was silently failing against a socket that didn't exist. TailscaleManager now starts tailscaled itself, idempotently, using the kernel TUN device docker-compose.yml already grants. Verified against a mocked tailscaled that reproduces the exact failure (tailscale up fails until the daemon's socket exists) — confirms the fix resolves it, not just papers over the symptom. ## 0.5.5.u — 2026-09-10 — FEATURE — Tailscale is now a Settings toggle, not just an env var - Reported directly: having to edit .env and restart the container just to turn Tailscale on was the opposite of "something simple." Settings → Server Network is now a real form — paste an auth key, flip the toggle, hit Save & Connect, and it's live immediately, no restart. Backed by two new AppSettings columns; the saved value now takes precedence over env vars at every startup. Verified end-to-end against a mocked tailscale binary, including a race condition caught and fixed during testing (the first version of "Save & Connect" reported success before the connection attempt had actually finished). ## 0.5.5.t — 2026-09-10 — FEATURE — server-side Tailscale/Headscale support - The server itself can now be reached over Tailscale, not just a device — closing a real gap where a device could already be reached from the server's own SSH terminal over Tailscale, but the server had no Tailscale awareness at all. New tailscale_manager.py, wired into startup/shutdown, driven by CHIRP_TAILSCALE_ENABLED/CHIRP_TAILSCALE_AUTH_KEY/CHIRP_TAILSCALE_LOGIN_SERVER/CHIRP_TAILSCALE_HOSTNAME. New read-only status panel in Settings → Server Network. Dockerfile now installs the tailscale client; docker-compose.yml adds the NET_ADMIN capability, /dev/net/tun device, and a state-persistence volume it needs. Verified against a mocked tailscale binary: connects, reports its IP, and correctly no-ops when disabled. ## 0.5.5.s — 2026-09-10 — PERF — Dashboard cache warmer now also keeps yard_pulse warm - yard_pulse() (species streaks + 30-day diversity) has had its own 45-second cache for a while, same as the stats/summary calls next to it — but was never added to the background warmer that proactively refreshes those other two every 30 seconds, so it always paid its own full query cost on the first Dashboard visit after the cache went cold. Added, matching the existing pattern exactly. ## 0.5.5.r — 2026-09-10 — BUGFIX — iNaturalist submission role check, and a silent failure on the Users list - Submitting to iNaturalist could fail with "requires admin or submitter role" for users who did hold that role. In chirp-server-instances the login-issued token never carried a role claim at all, so the check silently fell back to treating everyone as a viewer; the submit and comment endpoints now look up the user's live role from the database instead of trusting the token. Also fixed in chirp-server for consistency, and login() now sets the claim correctly too. - The Users page's list could fail to load (e.g. no active instance selected) with the failure swallowed entirely — an empty list with no indication anything was wrong. Now surfaces the real error. ## 0.5.5.q — 2026-09-10 — BUGFIX + FEATURE — server logs no longer drowned by a third-party library's own debug trace; Tailscale support rounded out - Two separate log attachments turned out to contain thousands of lines of numba's (a librosa dependency) own internal JIT-compilation trace and nothing else — no HTTP access log, no application events, nothing that helped diagnose what was reported. Traced to numba's own logger being set to DEBUG independently of this app's own logging setup, most likely by something in the deployment environment rather than this codebase. Fixed by explicitly capping numba's own logger at WARNING, unconditionally, regardless of what else sets it or how the app gets launched — a server log should now be usable for diagnosing whatever it was pulled for. - Rounded out the Tailscale/Headscale connectivity work from 0.5.5.p: chirp-device now cleans up its Tailscale connection on shutdown alongside everything else, and validates a placeholder auth key at startup instead of only discovering it later. The Devices page now shows a Tailscale badge on any device reporting one, and its SSH button is no longer hidden for a device that only has a Tailscale IP and no local one. ## 0.5.5.p — 2026-09-10 — FEATURE — import a database as a new location; spectrograms now correct themselves for the color-spread fix; early Tailscale support - New: create a location by importing an existing chirp-server database, not just starting empty — from both the login-time location picker and the Global Admin panel, which had no way to create or import a location at all before this despite the API already supporting it. - The detection-color spread fix (0.5.5.o) only invalidated a cached spectrogram's colors for a non-default palette — a recording using the default palette (the common case) would otherwise keep serving its old, sequentially-colored spectrogram forever, since nothing about its own cache key ever changed. Added a one-time, startup-triggered background sweep that force-regenerates every existing recording's spectrogram once, then marks itself done so it never repeats. Verified directly: an old cached spectrogram gets replaced with a freshly-generated one, the completion flag is set, and re-running the sweep afterward is a safe no-op. - Early support for reaching a device over Tailscale/Headscale, not just the local network: devices can now report a second, stable IP over heartbeat, and the SSH terminal can connect through either one, with a Local/Tailscale toggle in the terminal itself when both are available. chirp-device gained a config.yaml section and install step for this, off by default. Full remote-management UI (a dedicated status indicator, a way to reach the server itself the same way) is still in progress — this is the connectivity layer, not the finished feature. ## 0.5.5.o — 2026-09-09 — FEATURE (chirp-server, chirp-server-instances) — selectable detection color palettes, with colors spread by species count - Reported directly, with a screenshot: two species in one recording (Downy Woodpecker, Cooper's Hawk) got assigned red and orange — adjacent, similar-looking hues on the fixed 12-color wheel every detection color came from. The wheel itself wasn't the problem — colors were assigned sequentially in detection order (first species gets color 0, second gets color 1), so any recording with only a few species always landed on whichever colors happen to sit next to each other at the start of the wheel. - Fixed by spreading assigned colors evenly around the wheel based on how many species are present — 2 species now land on opposite sides, 3 species land 120° apart, and so on. Verified directly: the same two species now get red and cyan instead of red and orange. - Added four selectable palettes in Settings — Vivid (the original), Colorblind Safe (the established Okabe–Ito palette), Pastel, and High Contrast — applied consistently across the detection list, waveform, and the spectrogram's own baked-in overlay colors (the Python and JS implementations of the spread algorithm were verified to produce byte-identical output for the same input). Switching palettes also correctly invalidates any already-cached spectrogram image, so a stale image never silently keeps showing the old colors. ## 0.5.5.n — 2026-09-09 — FEATURE (chirp-server, chirp-server-instances) — automatic cleanup for orphaned Live Detect poll clips - Hiding individual, unsaved Live Detect poll clips from the list and dashboard stats (0.5.5.m below) stopped them from cluttering the view, but didn't stop them from piling up in the database and on disk indefinitely if a session is never explicitly saved. - Added a background sweep, once an hour, that removes any individual poll clip older than 24 hours — long enough that an in-progress session (someone on an hours-long walk) is never mistaken for an abandoned one, short enough that orphaned clips don't linger for days. Removes both the database row and its audio file. A saved session's own consolidated recording is a completely different, unaffected row — this only ever touches the small, per-poll clips that were never rolled into one. - Verified directly: an old orphaned clip and its file are both removed; a recent clip (a plausibly still-active session) is left alone; a normal recording is never touched regardless of its own age; running the sweep again once nothing is left is a safe no-op. ## 0.5.5.m — 2026-09-09 — BUGFIX (chirp-server, chirp-server-instances) — individual Live Detect poll clips could bury every other recording in the list - Reported directly from a deployment: leaving Live Detect running for a couple of minutes created hundreds of tiny, individual recordings — one per poll — none of which ever get cleaned up unless the session is explicitly saved. With no session saved, every one of them stayed in the main Recordings list and dashboard counts permanently, indistinguishable from a normal recording except by filename. - Fixed by marking these individual clips explicitly at creation time and excluding them from the main recordings list and dashboard stats, the same way Haikubox rows are already kept out of the main feed. A session that IS saved is unaffected — the one consolidated recording it produces still shows normally, only the leftover per-poll clips are hidden. - Verified directly: a normal recording and a saved session both still appear and count correctly; an individual, unsaved poll clip does not appear anywhere and is not counted in dashboard totals. ## 0.5.5.l — 2026-09-09 — BUGFIX (chirp-server, chirp-server-instances) — spectrograms/audio playback returning 401; a successful Field Guide refresh could still show as empty - Spectrograms and audio playback render through a plain ``/`