The speech section shipped 6 Nov was doubly broken: LibreChat POSTs to
speech.*.openai.url VERBATIM in 0.8.8, so the base '/v1' URL 404'd, and the
STT_API_KEY/TTS_API_KEY env values held a key that no longer exists on the
gateway (401 token_not_found). Also:
- STT: tinfoil/voxtral-small-24b (token-priced; gateway bills ~$0.00008/request,
125x under the flat $0.01/request whisper; same TEE provider, verified equal
quality on dictation clips). Whisper remains allowed on the key as fallback.
- TTS voices: full provider speaker list (an unknown voice 400s at the gateway).
- speechTab: object form seeds Settings->Speech defaults (engineSTT/engineTTS
external, voice casual_female) for members who never saved their own.
- new gateway key: alias librechat-speech, $5/30d, audio models only, injected
as STT_API_KEY/TTS_API_KEY app env.
Verified on a throwaway librechat-test install then live (chat.inference.coop):
config/get serves the defaults; /api/files/speech/stt returns a correct
transcript; /api/files/speech/tts/manual returns audio. Known gap (gateway-side,
NOT this repo): aspeech rows log $0 spend — voxtral-tts is unmetered on the
gateway's aspeech path while the co-op pays ~$0.002/request.
A modelSpec webSearch:true forces web_search into the ephemeral agent on every
new conversation and re-applies it on context re-keys (the client's spec effect
does not consult per-conversation toggle state for new convos), so the model
could call web_search (SearXNG) with the member's toggle OFF. The toggle state
and the ephemeral agent are the same store, but the spec default kept winning.
Remove the spec-level default so the toggle alone decides; interface.webSearch
permission, defaultPinnedTools badge and the searxng webSearch block stay.
Server-side needs no change: @librechat/api never injects search from specs.
The key is auto-seeded within 10 minutes of first login by sync-keys.js, but a new user's first screen is the API-keys panel, which reads as mandatory. The welcome line is the surface we control; the settings dialog builds its strings from templates in a component chunk with no en locale bundle, so it is not cleanly patchable. Option 1 of three in the issue, approved by the GM.
With enforce: true (in place since at least Oct 23), every send whose body
carries no `spec` is rejected by api/server/middleware/buildEndpointOption.js
with "No model spec selected", surfaced to members as "Inference Co-op could
not complete this request."
400 of 547 conversations across 37 users had no `spec` — all created before
the spec system (newest Oct 20). They fail on the members' first send;
new chats were fine because the softDefault spec pre-selects.
enforce: false lets those conversations send with their own stored model
exactly as before Oct 20. prioritize stays true, so the spec picker is still
the primary chooser — it is just no longer mandatory. Verified: the change is
read at boot (no watcher in the image), so the app was restarted; the served
stylesheet md5 is unchanged by the restart; the mirror is byte-identical to
the deployed file (sha256 219dbdf2ca0b64f8…). Backup of the previous config
stays on the volume as /app/data/librechat.yaml.bak-20261027T221149Z.
If enforcement is ever restored, backfill `spec` on those conversations first,
mapping by each conversation's own `model` (only exact matches — a mismatch
triggers "Model spec mismatch" instead).
- CloudronManifest.json: byte-identical copy of the OFFICIAL appstore manifest
(the hand-edited one stripped addons and crash-looped the app on 2026-10-18)
- Dockerfile: pinned base (202610150224320000), brand assets only + title sed
- librechat.yaml: schema 1.3.17; chat models only in picker (qwen-image moved
to the agent image tool); speech via gateway with capped key refs; SearXNG
web search; inference-docs MCP server; forward-compatible interface.theme
- mcp/server.py: docs MCP server (stdio, stdlib-only, tool search_docs)
- assets/: co-op logo + favicons/PWA icons