diff --git a/crates/buzz-relay/src/api/transcribe.rs b/crates/buzz-relay/src/api/transcribe.rs index a7f1746da..5bba57b41 100644 --- a/crates/buzz-relay/src/api/transcribe.rs +++ b/crates/buzz-relay/src/api/transcribe.rs @@ -184,12 +184,11 @@ async fn authenticate( })?; let url = super::bridge::nip98_expected_url(&state.config.relay_url, &tenant, path); + // Always require NIP-98 signed auth for transcribe endpoints — these mint + // billable OpenAI sessions, so we cannot trust the unauthenticated X-Pubkey + // dev fallback (which is spoofable) regardless of BUZZ_REQUIRE_AUTH_TOKEN. let (pubkey, event_id_bytes) = super::bridge::verify_bridge_auth( - headers, - method, - &url, - None, - state.config.require_auth_token, + headers, method, &url, None, true, // force NIP-98 — billable endpoint )?; super::bridge::check_nip98_replay(state, &tenant, event_id_bytes).await?; diff --git a/desktop/src/features/dictation/ui/DictationButton.tsx b/desktop/src/features/dictation/ui/DictationButton.tsx index a1151bdc1..387a0ee32 100644 --- a/desktop/src/features/dictation/ui/DictationButton.tsx +++ b/desktop/src/features/dictation/ui/DictationButton.tsx @@ -28,11 +28,13 @@ export function DictationButton({ ? "Transcribing…" : "Dictate message"; - // Allow the stop action even when the composer is disabled — the user must - // always be able to stop an active recording session. Only block *starting* - // a new recording when disabled. - const isDisabled = - dictation.isStarting || (disabled && !dictation.isRecording); + // Allow the stop action whenever the mic is live (isRecording), even if + // the session setup is still in progress (isStarting) or the composer is + // disabled. Only block the button when idle + disabled, or when startup + // hasn't captured the mic yet (isStarting && !isRecording). + const isDisabled = dictation.isRecording + ? false + : disabled || dictation.isStarting; return (