Repository navigation
Nojoin 3.0.0
Nojoin 3.0.0
Container images for this release. All images are cosign-signed and ship build-provenance and SBOM attestations; verification steps are in the deployment guide. Pin to a digest for reproducible deployments.
ghcr.io/valtora/nojoin-api@sha256:892c7aefb73fb6744146647277cbd3f4e51a86a364f5cb04e5f559839663d45bghcr.io/valtora/nojoin-worker@sha256:336063657cfe59d3b4eff53e7248105f993083c46b195c529af1a1865fd0572dghcr.io/valtora/nojoin-worker-io@sha256:2ac054eedb361263ce0ff7de6891ecdd5d71aa02e184cd6adfed2152c53ea1a3ghcr.io/valtora/nojoin-frontend@sha256:926ac75196cb7be81424fb288516300d9848a4bbfc04eb01aa9b03d3c7bf993f
Breaking Changes
- Your notes are now timeline notes. Each note sits at a moment in the meeting, survives reprocessing and reaches the AI at that point. Free-text notes from earlier releases become one note before the transcript (#494).
- Integrations that use notes need updating.
/transcripts/{id}/timeline-notesreplaces/transcripts/{id}/user-notes, MCPget_meeting_notesreturnstimeline_notes, andappend_meeting_notesadds one note per call, atat_secondswhen given. Backups are written in format 3, which earlier releases refuse (#494, #684). - Some other API answers changed.
POST /tags/answers 400 for a name already in use (#604), batch recording routes answer withcount,skippedandrecording_ids(#739), and edits answer 409 while a reprocess runs (#756). The OAuth token endpoint answers a missinggrant_typewith an OAuth error (#648), and every retired Companion route answers 410 (#665). - Actions that could lose a meeting now refuse. Retry Speaker Inference answers 409 unless the meeting has finished processing, and Discard answers 409 for any meeting that has finished once, so move it to the bin instead. Reprocess answers 409 for a paused capture or while notes generate, and notes generation answers 409 during a capture. Notes no longer move a recording to processing, and MCP tools pass on a route's reason when it refuses (#850, #852, #855, #857, #859).
- Parakeet word ends are capped, at 0.4 s by default. A word also ends where the next one starts or its window ends, so a window's last word no longer runs into the next speaker's turn. Word length cap (
asr_word_length_cap_s) replaces the retiredasr_word_end_padding_s(#473). - Several defaults changed.
- Browser noise suppression is off, and a setting saved before this release reads as off once (#711).
- Live speaker labels and live word settling are on with a CUDA worker (#729, #735).
- Speaker separation hears the whole recording, which tells people apart better and can take up to 1.5 times as long on a CPU (#658).
- Suggested speaker names wait for Confirm, and only confirmed names teach a voiceprint (#683).
- New transcript lines run to about 30 seconds, up from 10 (#735).
- Edit text moved to each line's ⋯ menu, and clicking a line selects its text (#616).
Highlights
- One recording view for every state. Recording, paused, processing and finished meetings open on the same page, with the recorder in the player's place and one side column for Speakers, My notes, Captures and Meeting Edge (#756).
- Live words settle before they lock. On a GPU server recent words are decoded again until three passes agree, so they appear about 2.3 seconds sooner and are as accurate or better. Live lines can be edited and split once they lock, and reprocessing keeps your corrections (#735).
- Live lines name their speakers. With a CUDA worker live lines show "Speaker N", and you can name a speaker while recording. NVIDIA's Nemotron model labels meetings of up to seven people, live and final, and a voiceprint match of 0.85 or more shows its name without asking (#543, #696, #705, #725, #729).
- Live speakers can be picked and merged. During a capture Change speaker lists the meeting's live speakers first, and one live speaker can be merged into another from a line or from Speakers. Both carry through to the processed meeting. People appear in the picker only when you search (#863, #866).
- Notes go on lines, selections and moments. Add one under the live transcript, from a line's ⋯ menu or on selected text, move it between lines, and find it again in the My notes tab. Unsaved notes survive a reload, and exports include your notes unless you untick Include my notes (#517, #553, #613, #616, #684, #690, #694).
- Captures put screen stills and files on notes. Alt+Shift+C or the camera button saves a still of the shared screen, and a file dropped into the note box goes with its note. Notes, Meeting Chat and search read only the files you switch on, and a file can be removed from its note (#699, #738).
- A recording that needs recovery gets a badge. Pausing in one tab no longer opens a dialog in every other tab, and a reloaded capturing tab takes its capture back at once (#760).
- The microphone can be released while paused. A per-browser setting, off by default, frees it for other apps during a pause and asks for it again on Resume (#755).
- Browser recordings hold up through more failures. A capture survives an ended sign-in, an outage and a takeover by another tab, and audio a tab could not upload is offered as a download. The live transcript says when it is behind, and moves past a missing chunk where it used to stop (#518, #581, #591, #629, #790, #791).
- Each recording can have its own language. Set it at import or on reprocess. With Whisper and voice activity detection on, Nojoin detects the language from speech and offers to reprocess when it hears a different one. Canary on automatic now reads as English (#673, #680).
- Transcripts lose fewer words and invent fewer. Transcription hears the audio inside every speaker turn, Parakeet decodes speech it skipped a second time, and Whisper's "Thank you." in digital silence is dropped (#496, #609, #675).
- Voiceprints are harder to pull off course. A new voiceprint needs 20 seconds of clear speech from one recording, folding in another recording needs a similarity of 0.70, up from 0.60, and a voiceprint can be locked (#498, #499, #515).
- Two appearance styles join Default. Classic and Depth are under Settings > Appearance > Style, saved per browser (#723).
- Dates and times follow the time-zone setting everywhere. Outside UTC they were shifted by the browser's offset, and "Revised" stuck to finished lines. With no zone chosen Nojoin shows UTC, or
DEFAULT_TIMEZONEwhere the administrator set one, so pick yours in Settings > Appearance (#751, #753). - Recordings lists share one selection. Recordings, Archived and Deleted take Shift ranges and a ⋯ menu on each row, and every action but Delete Permanently offers Undo for 10 seconds. Phones get Speakers and My notes tabs and a Start button (#655, #684, #739, #802).
- Security gaps are closed. The bundled proxy sends HSTS on every HTTPS response (#493), a password change or revoke-all ends MCP access, refresh tokens included (#500), and file paths stay inside their folders (#630, #632). The images drop the distrusted eMudhra root (#588), logs mask one-time codes (#674), people and tags stay in each user's own library (#544, #548), and the last active Owner or superuser can no longer be removed (#641, #642). The worker images carry filelock 4.2.1 and dnspython 2.9.0, which fix a file overwrite and a denial of service (#870).
- Meetings are harder to lose. Retry Speaker Inference during a capture used to end it and lose its audio, and Discard while notes regenerated deleted the finished meeting. Notes, AI analysis and delivery left behind by a worker restart now read as failed after 15 minutes and can run again, where they stayed generating for good, and a retry whose audio is missing stays failed instead of waiting in the queue (#850, #852, #859).
- Setup follows a pinned telemetry setting. With
NOJOIN_TELEMETRY_ENABLEDset, setup shows its value read-only and stores no choice, so removing afalsepin later leads to the usual notice instead of diagnostics starting unasked (#854, #862). - Smaller fixes. Regenerating notes on several recordings no longer fails with "No model selected" (#471), and Stop no longer fails with a deadlock while the last segment is transcribed (#784). Meeting Chat no longer offers to watch the meeting or act later (#786). Images fit the vision providers' limits (#713, #721). Setup accepts a non-ASCII
FIRST_RUN_PASSWORD, and two browsers finishing it at once no longer make two Owners (#727). Calendars stop retrying a refused sign-in, and tags hold each name once per user (#593, #667). A second find and replace now changes the lines an earlier one changed (#857). A speaker correction carries only onto the words its line covers, so one near a change of speaker no longer takes the next person's speech with it (#864). The Reprocess dialog opens on the install's model, and unnamed buttons and the notes editor have names for screen readers (#855). Permanent and batch delete take their locks in the same order as Discard, so the two can no longer deadlock (#857). - Dependency changes. The worker pins Transformers 5.20.2 for Nemotron, and Pillow moves to 13.0.0 (#532, #725). PDFs are parsed with pypdfium2 and pdfplumber in place of PyMuPDF, so multi-column pages read column by column and a truncated PDF fails to parse. Documents attached earlier keep their text until parsed again (#651). Claude Sonnet 4.6, Opus 4.6 and Haiku 4.5 now use Anthropic's default temperature, which the current Anthropic API requires (#556).
Upgrade
Update your checkout, pull the new images, stop the stack, and start it again.
git pull
docker compose pull
docker compose down
docker compose up -d- Stop the stack before starting the new images. A 2.10.0 worker still running after the migration fails, because it reads the
transcripts.user_notescolumn this release drops (#494). git pullbrings the bundled proxy's configuration, which now includes two new files,nginx/hsts.confandnginx/access-log.conf. Stopping the stack recreates the nginx container, which it needs to read the new configuration. If an edge proxy in front of Nojoin already addsStrict-Transport-Security, remove one of the two, because a browser reads only the first (#493, #674).- A Canary install downloads the pinned Qwen3 forced aligner, about 1.8 GB, on
worker-cpuat first start, and needs about 3.7 GB more worker RAM while it is loaded. Canary transcribes without word timings until it is ready (#735). - A CUDA install downloads the Nemotron diarisation model, 397 MB, after its first eligible recording, which keeps the existing speaker pass. Prepare it ahead in Settings > AI providers > Advanced > Model downloads. CPU workers and
speaker_engineset toclassicnever use it, and recordings with Max Speakers set use it for live labels but not in final processing (#725, #729). - Final processing of long recordings can take longer on a CPU host, because speaker separation now hears the whole recording. Voiceprint match scores may shift slightly. Mute non-speech for speaker separation in Settings > Recording > Advanced brings back the old behaviour (#658).
- The example compose file now passes the four
NOJOIN_STALL_WATCHDOG_*variables toapiandNOJOIN_STORAGE_HYGIENEto every app service. Adocker-compose.ymlcopied from an earlier example ignores those values in.envuntil you copy the lines across (#511, #533). - An empty
LLM_PROVIDERnow means the provider of the only API key set, andgeminiotherwise. Adocker-compose.ymlcopied from an earlier example turns an empty value intogemini, so change its line to${LLM_PROVIDER:-}to use the new rule. If the provider changes, choose a model in Settings > AI providers (#521, #728). - Compose reads a
.envline such asKEY= # noteas the value# note. Nojoin now reads such a value as empty and logs the variables it emptied, so move each comment to its own line. IfBACKUP_EXPORT_DIRhad that shape, exports went inside the worker container and were lost when it was recreated, so export again any archive you need (#562). - Reprocess a recording to apply the Parakeet word cap, the longer lines and the new speaker pass to it. Recordings keep their stored transcripts until then.
Migration
Database migrations run automatically on the first API start after upgrading. Back up your instance before upgrading.
- Fifteen Alembic revisions run in order on the first API start.
7c3e5a91d2f4addsrecording_speakers.embedding_speech_seconds, the speech a voiceprint was built from (#498).63005cac6492turns each non-blanktranscripts.user_notesinto one note before the transcript, owned by the recording's owner, and drops the column (#494).7ff0bbdadad8adds the tablerecording_speaker_drift_refusals, empty at first (#499).7cee4b4388a7gives each person with no owner the owner of the recordings that link them, and moves a link into another user's library to the linking user's person of the same name. People made by Create Voiceprint or the demo meeting then show in their owner's People page. It deletes nothing (#544).f704bbd87f98gives each people tag with no owner an owner, and keeps tag links and parents within one user's library. It deletes no tag (#548).edb5059e5d68merges each user's repeated tag and people tag names into the oldest tag of that name, then adds a unique index on(user_id, name)(#593).8f7a09a9d7f3addsrecordings.capture_epoch, which names the tab that holds a capture (#591).fb5a849e7f5bdeletes repeated links between a meeting and a tag, keeping the oldest, then adds a unique constraint (#601).76243f67da02addsrecordings.transcription_language, empty, so every recording follows its owner's setting (#680).b13e4912f23aaddstranscripts.language_detection(#680).0f3b502561a6adds the tablespeaker_name_eventsandrecording_speakers.name_source. Names given before the upgrade show as they did (#683).c4d81e6a2f90addsdocuments.note_idanddocuments.use_in_notesfor files on notes (#699).7bb9c74f9946addstimeline_notes.file_removed(#738).f2a6e1d9c735addstranscripts.change_sequenceandtranscripts.settling_state(#735).53b4bc17207faddsrecordings.first_processed_atand fills it in for every recording that has finished processing, so Discard refuses them (#850).
asr_word_end_padding_sis retired. A stored value is ignored, and dropped fromconfig.jsonon the next save (#473).- New install-wide keys in
data/config.jsonarelive_speaker_labels(autooroff) andspeaker_engine(autoorclassic), which have no settings page, andlive_text_settling(off,autooralways) withlive_text_settling_k(3) andlive_text_settling_window_s(10). Without a stored value the three modes takeauto, and a storedoffstays off.alwayssettles on a CPU worker too, at a high CPU and RAM cost. Thelive_max_segment_sdefault rises from 20 to 30 seconds (#725, #729, #735). - New per-user settings are
asr_word_length_cap_s(0.4 s),line_break_pause_s(1 s),vad_mute_transcription(on),vad_mute_diarization(off),confirm_suggested_names(off) andvoiceprint_learning(confirmed). Settingvoiceprint_learningtoautomaticlets unconfirmed matches teach voiceprints again (#473, #658, #683, #735). NOJOIN_STORAGE_HYGIENEis a new environment variable. Empty meansflag, which only reports, anddeleteremoves the files (#533)..env.example,docker-compose.example.ymlandnginx/changed, as Upgrade describes.- Backups are written in format 3. Restoring an older archive turns its notes into one note before the transcript, as the migration does (#494). On recordings made without live word settling, a backup forgets the names you dismissed with Not Alex, so after a restore they can be suggested again (#683).
Rollback
- Roll the schema back before the images, because the 2.10.0 API refuses to start against a revision it does not know. With no browser recording in progress and 3.0.0 still running, run
docker compose exec api alembic downgrade 3b804f4435a1, then stop the stack and start the 2.10.0 image tags. - Keep a backup made on 2.10.0. That release refuses the format 3 archives 3.0.0 writes.
- The downgrade folds each meeting's notes back into one free-text field in timeline order, losing each note's kind, time, quote and author. Files on notes, screen stills included, stay on their meetings as ordinary documents, and every notes run reads them.
- It also drops speaker name sources, so a name still waiting for Confirm shows as a plain name. Drift guard refusals, each recording's own language, language detection results and voiceprint speech figures go too. Merged tags, deleted duplicate tag links and the owners given to people and tags stay as they are.
Known Issues
- After a pause, the transcript's line break can land a few seconds early, mid-speech, when audio from before the pause was still waiting to upload, for example after an upload stall, an offline pause or a reload. The final words are unaffected (#815).
- The first words of a reply can go to the previous speaker, and live lines can move words between speakers at a turn change before they lock. The phantom speaker filter can also drop a short interjection with its words, and setting either of its limits to 0 turns it off (#776, #813).
- After a damaged audio segment the live transcript waits about 10 seconds before it moves on (#808).
- With Canary, untimed words after a line you corrected can go into that line's review and out of the transcript. Within ten minutes of a failed aligner download, preparing Canary reports done without fetching the aligner (#788).
- While the Canary aligner and Nemotron download at the same time, Model downloads can show one's progress against the other (#783).
Browser-Capture Compatibility
- Supported browsers, operating systems and audio sources are unchanged. Recording needs Chrome or another Chromium browser on desktop, or Chrome on Android or iOS for the microphone alone, and Firefox and Safari still cannot record.
- Capture asks the browser for echo cancellation and auto gain, with noise suppression off unless you turn it on under Settings > Recording > Advanced (#711).
- Stop releases the microphone before the upload, and Release the microphone while paused frees it during a pause. Shared tab or screen audio is never released, and the release is untested on iOS (#518, #755).
- Start can use a saved microphone that the browser lists without an id, as Chrome does after Allow this time and a restart (#809).
- Only the tab that holds a capture shows its controls. Other tabs and devices read Live or Paused with the server's clock, and a refused Start says why, including when the recording is open in another tab (#756, #760).
- Each Resume starts a new transcript line, so speech either side of a pause no longer joins into one line (#802).
Changes
Breaking Changes
- notes: Timeline notes replace the transcript's free-text user notes (#494) (
6c7e3ba0) - asr: Cap Parakeet word ends at the next word, the window end and 0.4 s (#473) (
4f06a3b5)
New Features
- speakers: Merge live speakers and list a meeting's speakers first (#866) (
520ed802) - recordings: One recording view for every state (#756) (
a74e1baf) - live: Settle live text and speakers before lines lock (#735) (
9a3af671) - capture: Release the microphone while paused (#755) (
143be791) - ui: Add Classic and Depth appearance styles (#723) (
199e9d5c) - captures: Remove a note's file from its viewer (#738) (
4478295d) - live: Default live speaker labels to auto, list Nemotron (#729) (
660a1dba) - diarization: Nemotron for small meetings, live and final (#725) (
95e32e8a) - capture: Add the opt-in screen study frame sampler (#722) (
a61ee7dc) - captures: Attach screen stills and files to timeline notes (#699) (
05b58ca3) - speakers: Show a strong voiceprint match without asking (#705) (
3f3e7f56) - live: Name a speaker during capture (#696) (
106dfb87) - notes: Move point notes between transcript lines (#694) (
121e5913) - site: Link the live demo from the hero and the nav (#692) (
1342264d) - speakers: Time-anchored speaker names with sources (#683) (
04758c4c) - notes: List My notes beside the transcript and carry notes into exports (#684) (
60a468fb) - transcription: Per-recording language and detection notice (#680) (
274610d6) - processing: Mute transcription only where neither VAD nor diarisation hears speech (#675) (
4d439528) - notes: Highlight a selection note's words, found again after a reprocess (#659) (
d8ab848a) - pipeline: Choose VAD muting per stage, diarise unmuted by default (#658) (
ebf87973) - notes: Fold a line's saved notes into a count (#649) (
c2aed5b6) - recordings: Give Speakers its own tab on a phone (#655) (
47e17e30) - notes: A note on text selected in the transcript (#616) (
786c9970) - notes: A ⋯ menu on each transcript line, with "Note on this line" (#613) (
4f1dabbe) - capture: Offer audio left out of a recording as a download (#629) (
bf9abca5) - notes: Mark recordings with unsent notes kept in the browser (#596) (
e9d41e7f) - notes: Keep unsaved notes across reloads and ended sessions (#553) (
100c7493) - live: Provisional speaker labels in the live transcript (#543) (
d5b17e10) - notes: Add notes from the live transcript, and show them inline (#517) (
b521a672) - storage: Report and reclaim files that no row names (#533) (
08902b74) - speakers: Remember the drift guard's refusals, and rebuild a split's original from their other recordings (#499) (
f7e56af9) - speakers: Found library voiceprints on 20 s of speech, guard folds at 0.70 (#498) (
1f641efe)
Bug Fixes
- docker: Upgrade the worker base image's filelock and dnspython (#870) (
e803a3c3) - pipeline: Give an edited live line the final speaker of its words (#867) (
00ca8a14) - pipeline: Carry a chosen speaker only onto the words its line covers (#864) (
0a510d1e) - live: Change speaker can pick a live speaker, and finalisation keeps it (#863) (
b7d772aa) - setup: Record no telemetry choice when setup sends none (#862) (
6dfa5080) - recordings: Keep the finished view while a finished meeting reruns (#861) (
fc125dbf) - recordings: Recover side runs lost with their worker (#859) (
a4d1aaf8) - ui: Small fixes to recovery, live notices and control names (#855) (
56cdee7a) - recordings: Show a settled meeting's kept lines while it reprocesses (#858) (
d3b53413) - backend: Find and replace, MCP refusals, delete lock order (#857) (
ff98c8af) - setup: Show a telemetry value pinned by the environment read-only (#854) (
a21b90d5) - recordings: Refuse reprocess and notes during a live capture (#852) (
fbd81cbe) - recordings: Guard speaker inference and discard by meeting state (#850) (
2339c27c) - capture: Keep meter and clock updates out of page renders (#849) (
b2ac03fe) - recordings: Open captures on Transcript, no live feed for imports (#848) (
a3cbf8a5) - transcript: Stop an update loop when Revised badges expire (#843) (
553caa98) - ui: Four fixes from 3.0.0 smoke testing (#829) (
3d400037) - import: Take the audio lock before storing an import's audio (#814) (
7cc9b855) - recordings: Three small fixes from 3.0.0 testing (#802) (
d8b36693) - live: Say when the live transcript is behind the recording (#791) (
0965c10b) - api: Open the Docker client on first use, not at import (#810) (
1913853e) - capture: Let Start request a saved microphone the browser hides (#809) (
258e863b) - live: Skip a missing chunk once five later chunks are waiting (#790) (
dd786da5) - capture: Badge recordings that need recovery instead of interrupting (#760) (
d8497066) - chat: Say what Meeting Chat can do, so it stops offering what it can't (#786) (
9bed4b20) - capture: Take the audio lock before the live write (#784) (
3ecf8155) - ui: Pointer cursor on buttons and a usable collapsed recordings rail (#758) (
c7b9f15e) - ui: Show every date and time in the time-zone setting (#753) (
32424867) - ui: Interpret zone-less server timestamps as UTC (#751) (
fe4126bb) - ui: Keep each rail's edge on the pointer while resizing (#748) (
100558d0) - setup: Word a failed model download plainly and fit the model select (#746) (
f5df2c70) - setup: Let a reloaded wizard resume past the sign-in guard (#745) (
dac747d1) - chat: Land on the newest message once the side column settles (#744) (
661757ba) - Keep the test suite's config.json and keys out of the checkout (#743) (
da20e397) - recordings: Stop opening a recording from scrolling the app shell (#742) (
1538f9ff) - ui: Show Default while a slow optional style loads, check fill markers (#740) (
1deecbc9) - recordings: One multi-select model, bulk actions match single ones (#739) (
3fdedbeb) - notes: Return focus to the file's section after Remove (#741) (
329b0c1c) - setup: Say where Model downloads is and mention disk space (#731) (
1e706efc) - setup: Refuse the example password and resume a reloaded wizard (#728) (
d09cfc4c) - setup: Serialise owner creation and quieten a fresh start (#727) (
1fd4b68d) - documents: Split a page's pictures across vision calls (#721) (
1485264f) - transcript: Keep speaker focus after dismissing a name (#719) (
4a660e8f) - capture: Turn browser noise suppression off by default (#711) (
9fb64861) - documents: Fit images to the vision providers' limits (#713) (
595ded90) - site: Serve the app's variable Geist files and their licence (#717) (
bf031117) - storage: Delete restored audio under either recordings root (#704) (
f2a04833) - ui: Keep rings beside shadow-card in dark mode and Classic (#697) (
e6007ce5) - exports: Drop the User Notes section when my notes are left out (#690) (
3886acc7) - exports: Drop characters Word cannot hold from DOCX exports (#687) (
80f20e7e) - processing: Keep turns the phantom filter judged non-speech muted (#681) (
edfe98f7) - transcription: Detect Whisper's automatic language on VAD speech (#673) (
773b4aa9) - logging: Mask one-time codes in logged request URLs (#674) (
5c163238) - notes: Find a one-word quote inside a word only from three letters (#666) (
1e0dbf63) - calendar: Log and show why connecting a calendar failed (#669) (
80c5ecd3) - calendar: Stop retrying refused grants and failed push channels (#667) (
7a2ba976) - api: Answer 410 on every retired Companion route (#665) (
12aba5e4) - worker: Keep FastAPI out of modules the workers import (#663) (
40f7500a) - tour: Describe selecting text and the line menu in the transcript step (#656) (
7e0b4a62) - meeting-edge: Read text from wrapped questions and points (#654) (
24f552c9) - documents: Read PDFs with pypdfium2 and pdfplumber (#651) (
4b481657) - notes: Forget notes kept for a recording deleted on another device (#647) (
08731845) - users: Read the first Owner, keep one active superuser (#642) (
d803cd2c) - settings: Download all services' logs, keep one active Owner (#641) (
1f6438d5) - settings: Limit backup, logs and user edits to the setup account (#635) (
42796678) - release: Restore the changelog in the release notes (#638) (
f7822ed9) - notes: Forget notes kept for a recording deleted for good (#637) (
4d52be57) - backend: Confine stored and restored file paths to their folders (#632) (
05c20aaa) - import: Stop an unfinished import blocking Start Meeting (#631) (
d516cd32) - backend: Validate reprocess models, confine backup and media inputs (#630) (
ebefb3bf) - transcript: Count saves in flight, close only the editor that saved (#628) (
6d3b334e) - ui: Register Tiptap's Link once and ring focused picker days (#626) (
49f04680) - transcript: Stop search scrolling back to its match on every poll (#619) (
f6ef96b9) - ui: Keep focus on raw buttons busy with their own request (#625) (
1ff7d101) - live: Keep live labels off speakers, bound the label step (#561) (
23a0a22b) - ui: Label the date picker's selected and keyboard days by fill (#624) (
7fa07b2e) - ui: Keep ProseMirror's base styles under the CSP (#622) (
85f1d964) - ui: Wrap the live toolbar's speaker cap on a phone (#620) (
476ecb37) - ui: Theme the date picker's time list and arrow in dark mode (#618) (
41f84be7) - ui: Keep keyboard focus on a loading button (#615) (
959ee5d3) - transcript: Select the first match as typed, rename on one line (#594) (
dd460d73) - pipeline: Drop words in digital silence, give gap words a speaker (#609) (
79b08afc) - recordings: Stay on a recording when another account signs in (#587) (
c0a8cefb) - capture: Stand a tab down once its capture was discarded elsewhere (#608) (
5a8bf949) - capture: Back off an owed pause that keeps failing (#607) (
3fa5d246) - capture: Clear audio kept for a capture finished elsewhere (#606) (
ad83a40e) - tags: Refuse a new recording tag with a name already in use (#604) (
6085fbdb) - capture: Stand a tab down once its capture was stopped elsewhere (#603) (
bc05718f) - tags: Hold each tag on a meeting once in the database (#601) (
8ffcb2bc) - capture: Read paused as soon as the recorder pauses (#600) (
cc1e619d) - tags: Say why a tag rename, add or move was refused (#598) (
14b50de0) - capture: Refuse requests from a tab whose capture was taken over (#591) (
b55e68fd) - tags: Hold each tag name once per user in the database (#593) (
3a17c198) - docker: Remove the distrusted eMudhra root from the images (#588) (
8767fdc5) - backup: Restore an old archive's ownerless people and tags (#584) (
332bb80e) - recordings: Keep the segments when a move to failed/ raises (#576) (
26514a91) - capture: Keep a capture paused by an outage resumable (#581) (
767d4830) - documents: Import PyMuPDF under its supported pymupdf name (#577) (
83ed5825) - tags: Refuse tag loops and taken people tag names (#580) (
31dc12fa) - recordings: Unlink files only after the delete commits (#575) (
ac3bf978) - transcript: Build toolbar and play buttons on the primitives (#560) (
d653ea63) - capture: Offer a capture whose unload pause was refused (#572) (
ffdd3dd5) - backup: Remove the files of recordings an Overwrite replaces (#571) (
c1c58a98) - ui: Point users only at Settings pages that exist (#566) (
adb4a050) - config: Read a .env comment passed on as a value as empty (#562) (
7331760d) - storage: Refuse hygiene deletion when a directory clock lags (#552) (
0939a96b) - llm: Send no sampling parameters to Anthropic (#556) (
469b5391) - storage: Keep every file a hygiene run still needs (#541) (
d5cf04dd) - tags: Check tag ids against the user's own tags (#548) (
db76c337) - notes: Drop the deprecated Result.tuples() from the notes signature (#540) (
062fcbaf) - speakers: Scope voiceprint matching and links to the user's people (#544) (
4c483182) - speakers: A lock keeps every rename and promote out of a voiceprint (#515) (
9fa956ba) - speakers: Judge a speaker's new voiceprint against its person (#513) (
7ad01408) - docker: Clear the worker base Python's critical scan findings (#534) (
b747dd4f) - deps: Move Pillow to 13.0.0 past fastembed's cap (#532) (
87e5d1d7) - deps: Patch follow-redirects, markdown-it and telemetry's vite (#524) (
770ae41a) - deploy: Pass LLM_PROVIDER to the containers empty (#521) (
c638cd87) - capture: Keep recording through an ended sign-in (#518) (
e94e6f7c) - deploy: Pass the stall detection settings to the api (#511) (
e87ec4a7) - analytics: Leave the transcript's hidden UNKNOWN lines out (#509) (
0e1f576e) - speakers: Read a merged speaker's overlap labels as its survivor (#507) (
8fdc055d) - asr: Decode speech a Parakeet window skipped once more (#496) (
a01f8665) - documents: Remove attached documents' files when a recording is deleted (#495) (
969ebeac) - speakers: Stamp a voiceprint's version on every path that writes one (#497) (
5979192c) - oauth: Revoke MCP grants whenever token_version is bumped (#500) (
c405de61) - nginx: Send HSTS on every HTTPS response (#493) (
87bcec3c) - worker: Reload config.json before every task (#471) (
c34de242)
Performance
- frontend: One set of capture controls, and no re-render fan-out in notes and overlaps (#830) (
89dca144) - transcript: Keep unchanged segments across a full sync (#595) (
82e25eac) - transcript: Redraw only the lines a playback tick changes (#559) (
111342ee)
Documentation
- calendar: Name Cloudflare Bot Fight Mode as a live sync blocker (#677) (
13718b39) - Correct the stale code comments the docs review found (#567) (
7933f53e) - Check every guide against the code and cut a third of the words (#475) (
f3fb1666)
Other changes (26)
- release: Bump the version to 3.0.0 and write the notes (#819) (
79ff2b81) - Keep model caches and the Nemotron marker out of the real home (#736) (
ee30cd76) - site: Bump undici and wrangler for the undici advisories (#733) (
6a8b34e6) - Remove the opt-in screen study frame sampler (#732) (
770c8a06) - recordings: Flush effects before the 404 refresh event (#720) (
3082bd9b) - ui: Migrate capture and recording controls to native bases (#712) (
0a9e87c4) - speakers: Re-query the line before checking focus after a dismissal (#718) (
bc270a54) - frontend: Serve Geist from files in the repository (#715) (
ecb149e0) - ui: Route raw controls and surfaces through shared hosts (#708) (
56887bea) - Fail tests that reach outside hosts, add a fake LLM server (#706) (
bd7d99d7) - Keep the backend suite from downloading models (#691) (
6ef0173e) - release: Remove the release builder once the last bake is done (#688) (
f9ef82e3) - Share Postgres setup and run DOM-free vitest files in node (#682) (
ceb64a06) - Bump zod from 4.6.5 to 4.6.7 in /site (#672) (
afd8af3c) - site: Bump astro to 7.3.3 (#657) (
22aa3015) - Bump fastapi to 0.161.1 and skip PyMuPDF 2.0 (#648) (
e3d19599) - Bump lucide-react from 1.59.0 to 1.60.0 in /frontend (#644) (
1ddb9ed4) - Bump baseline-browser-mapping from 2.11.29 to 2.11.30 in /frontend (#643) (
654c3883) - lint: Run the pre-commit Ruff hook at the release CI pins (#565) (
3251c704) - worker: Drop the keep_models_loaded reads (#564) (
dd5b68f4) - pipeline: Remove the unused live-transcript reuse module (#563) (
47881b35) - deps: Consolidate the three open Dependabot updates (#555) (
a1b6b5ce) - transcript: Split TranscriptView and share one line renderer (#542) (
e3bcf667) - analytics: Remove the high-overlap attribution warning (#512) (
d1d6d059) - license: Use the canonical AGPL-3.0 text so GitHub detects it (#510) (
18f403a5) - site: Bump vite from 8.2.0 to 8.3.4 (#474) (
982c15c6)