The URL builder was prepending wss:// to the full https:// URL, producing
an invalid wss://https://... URL. Now properly converts https→wss and
http→ws before appending the /ws/transcribe path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove all signing env vars and setup steps. The local act runner's
keychain interferes with Tauri's auto-detection. Will re-add signing
once Apple Developer verification is complete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Run apply_settings in thread pool executor to prevent engine reload
from blocking the HTTP response (caused "TypeError: Failed to fetch")
- Flatten nested config objects into dot-notation keys before saving
so partial updates don't wipe out unincluded keys like auth_token
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use the existing /api/compute-devices response to determine if only cloud
is available, instead of relying on the backend's is_cloud_only status field.
Hides Local (Whisper) option when the sidecar only supports cloud.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents Tauri from auto-detecting local keychain certificates on the
build machine, which causes SecKeychainItemImport failures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Expose is_cloud_only flag in /api/status response
- Add isCloudOnly to backend store state
- Conditionally hide Local (Whisper) radio button in Settings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
hardenedRuntime triggers code signing which fails without a valid certificate.
Entitlements.plist and Info.plist remain for when signing is re-enabled.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set default server_url to https://transcribe.shadowdao.com
- Remove Server URL field from managed mode settings (users don't need to configure it)
- Replace Register button with link to website signup page
- Add fallback to default URL in login handler for existing users with empty config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI workflows now support code signing when secrets are configured:
- macOS: Apple Developer certificate + App Store Connect API key for notarization
- Windows: Azure Artifact Signing via signtool + dlib
- Both are no-ops when secrets aren't set (backwards-compatible)
- Add Entitlements.plist (mic, network) and Info.plist (NSMicrophoneUsageDescription)
- Add SIGNING.md with full setup guide for both platforms
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default remote.mode changed from 'local' to 'byok', causing
the apply_settings test to detect a mode mismatch and trigger an
unexpected engine reload. Pin remote.mode to 'local' in the test
to match the controller's assumed current mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change default transcription mode from local to byok (cloud/Deepgram)
- Move Transcription Mode selector to top of settings for visibility
- Hide local-only settings (model, VAD, timing) when cloud mode selected
- Disable Start button until API key (byok) or login (managed) is configured
- Add room creation and share code flow to Shared Captions section
- Add POST /api/create-room endpoint to Node.js sync server
- Update default sync URL placeholder to caption.shadowdao.com
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On macOS, sounddevice ships its own PortAudio dylib in the
_sounddevice_data directory. PyInstaller wasn't collecting it,
causing "Error querying device -1" when the sidecar tried to
open an audio stream.
Added data collection for _sounddevice_data in both cloud and
headless PyInstaller specs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On macOS, Cmd+Q triggers ExitRequested before Exit. If the app is
force-quit or closed via Cmd+Q, the Exit event may not fire,
leaving the sidecar process orphaned with ports 8080/8081 in use.
Now handles both ExitRequested and Exit to ensure the sidecar is
always stopped when the app closes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The version label was reading from backendStore.version which comes
from the sidecar's version.py (hardcoded at build time). Now uses
Tauri's getVersion() API which reads from tauri.conf.json -- the
actual app version that gets bumped by the release workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When apiPost throws (e.g. 400 "Already transcribing"), pollStatus
never ran because it was in the try block. The button stayed stuck
on "Start" even though transcription was running.
Moved pollStatus to the finally block so it always syncs the UI
with actual backend state. Also suppresses the error message for
400 responses since they just mean the state is already correct.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The line [ "\$HTTP_CODE" != "204" ] && cat ... returns exit code 1
when the condition is false (all dispatches succeeded). Since it
was the last command in the loop, the step reported failure.
Changed to if/then/fi which doesn't leak the test exit code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
start_transcription() blocks up to 15s waiting for the Deepgram
WebSocket to connect. Running it synchronously in the async endpoint
blocked the entire uvicorn event loop, preventing:
- pollStatus from completing (frozen HTTP request)
- WebSocket broadcasts from being sent
- Any other API requests from being handled
Fix: run start/stop/reload in thread pool via run_in_executor so
the event loop stays responsive during long-running operations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same fix as release.yml -- replaced step outputs with GITHUB_ENV
variables to avoid the act runner format bug. Also removed the
has_changes conditional since sidecar-release is now manual-only
(workflow_dispatch always means we want to build).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The act runner has a Go format bug that evaluates step outputs at
cleanup time and crashes with %!t(string=...), marking the job as
failed even though all steps succeeded.
Replaced steps.bump.outputs.* with GITHUB_ENV variables which
persist across steps without triggering the runner's output
evaluation bug.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Audio capture started immediately after spawning the WebSocket thread,
but the WebSocket hadn't connected yet. Audio chunks sent to the
unconnected WebSocket caused a broken pipe error.
Fix: added a threading.Event that start_recording() waits on (up to
15s) before opening the audio stream. The event is set in _ws_lifecycle
after the WebSocket connects and handshake completes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Start Transcription button now shows the error message when it fails
instead of silently reverting. Common causes:
- Missing PortAudio library on Linux
- Audio device not accessible
- Deepgram connection failure
Also added error details to backend console output and captured
the last error from the Deepgram engine for better diagnostics.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sidecar process was orphaned when the Tauri app closed, leaving
ports 8080/8081 in use. On next launch the new sidecar couldn't bind
those ports and failed to start.
Added RunEvent::Exit handler that stops the sidecar before the app
process terminates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Linux CPU sidecar: PyPI's default torch on Linux includes CUDA
(~800MB). UV_NO_SOURCES only bypasses our custom CUDA index but
still gets CUDA-enabled torch from PyPI. Now explicitly installs
CPU-only torch from pytorch.org/whl/cpu after sync. Same fix
applied to Windows.
New cleanup-releases.yml workflow (manual trigger):
- Configurable: keep N app releases, keep N sidecar releases
- Dry run mode (default) shows what would be deleted without deleting
- Protects v1.4.0 (last pre-Tauri release)
- Shows release sizes in MB
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CUDA sidecars are ~2GB and too slow to upload from the Windows runner.
Cloud (Deepgram) provides faster transcription anyway. Removed:
- CUDA build steps from Windows and Linux sidecar workflows
- CUDA option from the SidecarSetup download screen
Remaining sidecar variants:
- Cloud (Deepgram): ~50 MB - recommended for most users
- Local CPU: ~500 MB - for offline/privacy use
CUDA can be revisited once the managed Deepgram service is ready.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
zip -9 on Linux, 7z -mx=9 on Windows. Compression takes longer but
produces smaller files which upload faster over the network.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On first launch, the cloud sidecar now:
1. Detects it's the cloud variant (DeviceManager import fails)
2. Auto-switches config from "local" to "byok" mode
3. Shows "Setup needed: Open Settings > Remote Transcription >
enter your Deepgram API key" as a friendly status message
4. Stays in READY state so the UI is fully accessible
The user can then open Settings, enter their Deepgram API key,
save, and start transcribing without needing to know about modes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The cloud sidecar excludes the local Whisper engine module, but on
first launch the config defaults to remote.mode="local" which tries
to import it. Now catches the ImportError gracefully and shows an
error message telling the user to switch to Cloud (Deepgram) mode
in Settings. The API server still starts so Settings is accessible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>