Repository navigation
Own keys: ElevenLabs and Volcano Engine keys for spoken replies - #187
Merged
Merged
Conversation
The user's own key for a speech provider (ADR-0020) is kept by the daemon in config_dir()/voice-keys.json (mode 0600) and set with PUT /v1/voice-keys. A reply whose voice is from a provider with an own key goes straight to that provider and never to vibebuddy-api: ElevenLabs as mp3_24000_48, Volcano Engine's V3 stream as 24 kHz MP3 with its leading ID3 tag dropped. A 401 or 403 marks the key refused in /v1/status and leaves that reply silent, with no fallback to the service. The status says only which keys are set. The Mac and Linux Account tabs get "Your own keys": a field for each, a link to each provider's key page, "Set" in place of the value, and Clear. Closes #182 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
This was referenced Oct 24, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #182 (parent spec second-state/vibebuddy-cloud#16, "Own keys"; decision ADR-0020 in #179).
What changes
Daemon
voice_keys.rs:config_dir()/voice-keys.json,{"elevenlabs"?, "volc"?}, written 0600 withwrite_private. It is a file of its own, asspeech.jsonis, soPUT /v1/configcan't wipe it.PUT /v1/voice-keyssets or clears each key. A string sets it;nullor""clears it; a provider left out keeps its key; an unknown name or value gets a 400. The answer has the same shape as the status field./v1/statusgetsvoice_keys: {"elevenlabs": {"set", "refused"}, "volc": {"set", "refused"}}. It never contains a key.synthesis.rs: a newSynthesisdispatcher. If the voice's provider has an own key, the reply goes straight to that provider and never to vibebuddy-api. Otherwise it goes to the service as before./v1/text-to-speech/<voice>/stream?output_format=mp3_24000_48witheleven_flash_v2_5, the same request the service makes.seed-tts-1.0, asking for MP3 at 24 kHz. The daemon reads its back-to-back JSON objects and drops the leading ID3v2 tag, wherever the pieces split it.refusedin the status and ends that reply's audio with"error":"synthesis". There is no fallback to the service. Taking the key again, or changing it, clears the flag. The status stream is told through a watch channel.Synthesizer::speakskeeps every other reply text-only, sending nothing, as before.Apps (Mac and Linux, in step)
docs/app.md, and own-key synthesis indocs/resident-session.md.Volcano Engine MP3 at 64 kbps
vibebuddy-cloud found that Doubao's MP3 comes at 64 kbps whatever
bit_rateasks, behind an ID3 tag, so the service asks for PCM and encodes it itself. For an own key I checked the box's player (firmware-rs/core/src/spoken_reply.rs) instead of adding an encoder:MAX_REPLY_BYTES. At 64 kbps that 256 KB holds about 32 s ahead instead of 43 s.sample_ratetoo.Verification
cargo test(workspace): all pass. New tests run against fake provider HTTP servers:PUT /v1/voice-keyssets, clears and rejects bad input.voice_keys, including an older daemon without it.cargo clippy --all-targets: no new warnings. The three that remain were there before, inactivity.rsandsession_titles.rs.swift build --package-path app: clean.swift run --package-path app SelfTest: passed, with a new decode check for own keys.python3 tools/check-localization.py: ok.🤖 Generated with Claude Code