From d847fbbcaca005b93f15bb9d072f0e86ab378098 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:00:54 +0200
Subject: [PATCH 01/15] fix(docs): send the Object Structure card and legacy
/toolkit URLs to real pages
The Object Structure card on the librechat.yaml page linked to
/docs/configuration/librechat_yaml/object_structure, a nav-only folder with
no index page, so the one card promising the "complete field reference"
404'd. Point it at the Config Structure page the section actually opens
with, and redirect the folder URL there for anyone who typed or bookmarked
it.
The legacy /toolkit redirects pointed one dead spelling at another
(/toolkit/yaml_checker -> /toolkit/yaml-checker); neither exists since the
toolkit moved under /docs/toolkit. In-page links survive because
lib/localize-href.ts canonicalizes them at render time, so this only bit
direct hits, including the one absolute toolkit URL in the Linux guide
that skipped canonicalization for being external-shaped. Redirect targets
now match the canonical map in localize-href.ts.
Feedback: 1531982915729359019, 1491241561986564299
---
.../docs/configuration/librechat_yaml/index.mdx | 6 +++++-
content/docs/remote/docker_linux.mdx | 2 +-
next.config.mjs | 17 +++++++++++++++--
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/content/docs/configuration/librechat_yaml/index.mdx b/content/docs/configuration/librechat_yaml/index.mdx
index 82ba1f429..ca5b32369 100644
--- a/content/docs/configuration/librechat_yaml/index.mdx
+++ b/content/docs/configuration/librechat_yaml/index.mdx
@@ -177,7 +177,11 @@ For detailed field-level documentation, see the reference pages below.
Compatible AI providers and example endpoint configurations
-
+
Complete field reference for every librechat.yaml option
diff --git a/content/docs/remote/docker_linux.mdx b/content/docs/remote/docker_linux.mdx
index 1ec86324d..7f8a5b607 100644
--- a/content/docs/remote/docker_linux.mdx
+++ b/content/docs/remote/docker_linux.mdx
@@ -213,7 +213,7 @@ cp .env.example .env
For production, generate permanent values instead:
-[https://www.librechat.ai/toolkit/creds_generator](https://www.librechat.ai/toolkit/creds_generator)
+[Credentials Generator](/docs/toolkit/credentials-generator)
```bash
nano .env
diff --git a/next.config.mjs b/next.config.mjs
index 4cb437b21..ee5e4032f 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -59,8 +59,21 @@ const nonPermanentRedirects = [
['/docs/features/plugins', '/docs/features/agents'],
['/docs/features/speech-to-text', '/docs/configuration/stt_tts'],
['/docs/configuration/librechat_yaml/setup', '/docs/configuration/librechat_yaml'],
- ['/toolkit/yaml_checker', '/toolkit/yaml-checker'],
- ['/toolkit/creds_generator', '/toolkit/creds-generator'],
+ // The toolkit pages live under /docs/toolkit; these are the pre-Fumadocs URLs
+ // still linked from older posts and bookmarks. In-page links are canonicalized
+ // at render time by lib/localize-href.ts, but a direct hit only has these, so
+ // both spellings of each slug have to land on the real page rather than on the
+ // other spelling.
+ ['/toolkit', '/docs/toolkit'],
+ ['/toolkit/yaml_checker', '/docs/toolkit/yaml-validator'],
+ ['/toolkit/yaml-checker', '/docs/toolkit/yaml-validator'],
+ ['/toolkit/creds_generator', '/docs/toolkit/credentials-generator'],
+ ['/toolkit/creds-generator', '/docs/toolkit/credentials-generator'],
+ // Nav-only folder: the section's landing page is the Config Structure reference.
+ [
+ '/docs/configuration/librechat_yaml/object_structure',
+ '/docs/configuration/librechat_yaml/object_structure/config',
+ ],
]
/**
From 696ff430a898b855dbb066974765d059fe8173d5 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:00:55 +0200
Subject: [PATCH 02/15] docs(config): document the speechTab fields and
defaultParamsEndpoint values
The speechTab section showed an example with eleven keys and explained
none of them. Add the field reference from the schema: accepted engine
values for STT and TTS, and the two settings whose meaning is not
guessable: decibelValue is a silence threshold in dB (-100 to -30,
default -45) and autoSendText is a delay in seconds where -1 disables
auto-send.
Neither speech page mentioned allowedAddresses, though speech.stt and
speech.tts both take one and every self-hosted example we ship
(host.docker.internal, localhost) is inside the private address space the
connect-time guard blocks.
defaultParamsEndpoint is z.string().default('custom'), not an enum, and
the page only showed 'google' as an example. The panel resolves it via
paramSettings[key] ?? [], so an unrecognized value passes validation and
renders an empty parameter panel instead of reporting an error. List the
keys that resolve and describe that failure mode.
Feedback: 1543536529195012169, 1506499310035140753
---
.../object_structure/custom_params.mdx | 21 +++++++
content/docs/configuration/stt_tts.mdx | 60 +++++++++++++++++++
2 files changed, 81 insertions(+)
diff --git a/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx b/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
index 764594a28..e45c6e4fd 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
@@ -19,6 +19,27 @@ endpoints:
Your "Google Gemini" endpoint will now display parameters for Google API when you create a new agent or preset.
+#### Accepted Values
+
+`defaultParamsEndpoint` selects which built-in parameter set the endpoint's panel renders. These are the values that resolve to one:
+
+
+
+
+
+The field is a free-form string, not a closed enum, so a value outside this list passes configuration validation. It simply matches no parameter set, and the endpoint's panel renders with **no parameters at all** rather than reporting an error. If your parameter panel is unexpectedly empty, check this value first.
+
+
+
### Overriding Parameter Definitions
On top of that, you can also fine tune the parameters provided for your custom endpoint. For example, the `temperature` parameter for google endpoint is a slide with range from 0.0 to 1.0, and default of 1.0, you can update the `librechat.yaml` file to override these values:
diff --git a/content/docs/configuration/stt_tts.mdx b/content/docs/configuration/stt_tts.mdx
index deca94586..1e5b96343 100644
--- a/content/docs/configuration/stt_tts.mdx
+++ b/content/docs/configuration/stt_tts.mdx
@@ -28,6 +28,22 @@ TTS_API_KEY=your-tts-api-key
These keys are then referenced in your `librechat.yaml` configuration using `${STT_API_KEY}` and `${TTS_API_KEY}`.
+### Self-hosted engines need an allowedAddresses entry
+
+Both `speech.stt` and `speech.tts` accept an `allowedAddresses` list. Outbound speech requests are validated against their resolved IP and blocked from reaching private, loopback, and link-local address space, so a self-hosted engine on `localhost`, a LAN address, or a Docker service name is unreachable until you list it:
+
+```yaml filename="librechat.yaml"
+speech:
+ tts:
+ allowedAddresses:
+ - 'host.docker.internal:8080'
+ localai:
+ url: 'http://host.docker.internal:8080/tts'
+ # ...
+```
+
+Entries are bare `host:port` pairs -- no scheme or path, port required, IPv6 bracketed as `[::1]:8080`, and IP literals must be private. Cloud providers such as OpenAI, Azure, and ElevenLabs need no entry. The same field and rules apply under `speech.stt`. See [SSRF protection](/docs/configuration/librechat_yaml/object_structure/web_search#ssrf-protection-and-private-providers) for the full entry format.
+
## Speech Tab (optional)
The `speechTab` menu provides customizable options for conversation and advanced modes, as well as detailed settings for STT and TTS. This will set the default settings for users
@@ -56,6 +72,50 @@ speech:
cacheTTS: true
```
+`speechTab` sets the initial values users see; each remains changeable per user in the speech settings tab.
+
+**Top-level keys:**
+
+
+
+**`speechToText` subkeys:**
+
+
+
+**`textToSpeech` subkeys:**
+
+
+
+
+
+`engineSTT` / `engineTTS` only choose which engine the UI starts on. Anything other than `browser` still needs the corresponding provider configured under `speech.stt` or `speech.tts` -- see the sections below.
+
+
+
## STT (Speech-to-Text)
The Speech-to-Text (STT) feature converts spoken words into written text. To enable STT, click on the STT button (near the send button) or use the key combination ++Ctrl+Alt+L++ to start the transcription.
From 14b4727753bca88b0cda6586c00ff7e4612e6486 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:00:55 +0200
Subject: [PATCH 03/15] docs(ollama): add troubleshooting and caption the
stop-sequence screenshot
The Ollama reports were about diagnosis rather than the sample config. Add
the checks that resolve most failures: container-vs-host baseURL, Ollama's
default 127.0.0.1 bind, and the name-prefix rule behind model fetching:
models.ts reaches for /api/tags first only when the endpoint name starts
with "ollama". Also
note that a remote or hosted server differs only in baseURL and apiKey.
The stop-sequence screenshot had "image" as its alt text and no caption
saying which panel it shows.
Feedback: 1539423034283393146, 1529194857564995645, 1528452749685952575
---
.../librechat_yaml/ai_endpoints/ollama.mdx | 20 +++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
index 5c5923935..78f7791e4 100644
--- a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
+++ b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
@@ -84,8 +84,24 @@ If you only run `llama3` with Ollama, setting `stop` at the config level via `ad
modelDisplayLabel: "Ollama"
```
-Set the stop sequences in conversation parameters (and save them as a preset):
+Set the stop sequences in conversation parameters (and save them as a preset). Open a conversation on the Ollama endpoint, open the right-hand parameters panel, and add each sequence under **Stop Sequences**:
-
+
+
+## Troubleshooting
+
+### Ollama does not appear, or the model list is empty
+
+Work through these in order:
+
+1. **Check the endpoint is reachable from LibreChat, not from your shell.** If LibreChat runs in Docker, `localhost` is the API container itself, not your host. Use `http://host.docker.internal:11434/v1/` on Docker Desktop, or the host's LAN address on Linux where `host.docker.internal` may be unavailable. Running LibreChat outside Docker is the only case where `http://localhost:11434/v1/` is correct.
+2. **Confirm Ollama is listening beyond loopback.** By default Ollama binds to `127.0.0.1`, which a container cannot reach. Set `OLLAMA_HOST=0.0.0.0` in Ollama's own environment and restart it.
+3. **Keep the endpoint named `Ollama`.** Model fetching has an Ollama-specific path that is selected by name: LibreChat only queries Ollama's `/api/tags` when the endpoint `name` starts with `ollama`, case-insensitively. Rename it to something else and `fetch: true` falls back to the generic OpenAI-compatible `/models` call, which Ollama does not serve the same way, so the list comes back empty.
+4. **Set `apiKey` to any non-empty placeholder.** Ollama ignores the value, but a custom endpoint with no `apiKey` is dropped at config load.
+5. **Read the API logs.** `docker compose logs api` reports the connection error and the URL it actually tried.
+
+### Using a remote or hosted Ollama server
+
+Nothing is local-specific except the URL: point `baseURL` at the remote server's OpenAI-compatible path and supply whatever credential that server expects in `apiKey` instead of the placeholder. Keep the endpoint name starting with `ollama` if you want model fetching to keep working.
From 918f18c447f26e3ca8974e5369ae5184b6f00263 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:00:56 +0200
Subject: [PATCH 04/15] docs(features): answer recurring questions on banners,
export, and search
Banner: update-banner does findOne() then findByIdAndUpdate, so there is
only ever one banner and running it again overwrites the previous message
and schedule. Two overlapping windows are therefore not possible, and
delete-banner is the only way to take one down early.
Import Conversations: the "Export your conversations" heading covered
exporting *from* ChatGPT and Claude, so readers looking for LibreChat's own
export found the wrong thing. Rename that section and document the real
export flow, its five formats, and the branch and option rules that vary
by format.
Message Search: say plainly that conversation search is keyword-based and
that semantic retrieval applies to uploaded files through the RAG API,
rather than leaving readers to infer it from "Meilisearch".
Transactions: the setting only controls whether records are written, which
is why enabling it does not make token counts appear anywhere. Point at
interface.contextUsage and contextCost for the display.
Feedback: 1494744632884596746, 1528752311281647727, 1508348821095059487,
1508345688273191073, 1522687279234289807
---
content/docs/configuration/banner.mdx | 10 ++++++
.../object_structure/transactions.mdx | 8 +++++
content/docs/features/import_convos.mdx | 33 +++++++++++++++++--
content/docs/features/search.mdx | 8 +++++
4 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/content/docs/configuration/banner.mdx b/content/docs/configuration/banner.mdx
index 11ac5287c..e155708ee 100644
--- a/content/docs/configuration/banner.mdx
+++ b/content/docs/configuration/banner.mdx
@@ -52,6 +52,14 @@ Is persistable (cannot be dismissed) (y/N):
- **Is persistable**: If yes, users can't dismiss the banner — use for important notices
+
+
+There is only ever one banner. `update-banner` looks for an existing banner and overwrites it in place, creating one only when none exists -- so you cannot queue up a second banner for a later date, and you cannot have two scheduled windows overlap. Running the command again discards the previous banner's message and schedule.
+
+To change a banner, run `update-banner` again. To take one down before its `Display To` time, use `delete-banner` below.
+
+
+
---
## Deleting a Banner
@@ -62,6 +70,8 @@ npm run delete-banner
You'll see the current banner and be asked to confirm before deleting.
+Removing a banner this way is also how you take one down early -- there is no "disable" flag. A banner otherwise disappears on its own once `Display To` passes, and one saved with no `Display To` stays up until you delete it.
+
---
## Example Banners
diff --git a/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx b/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx
index d63908200..ffccc5671 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx
@@ -8,6 +8,14 @@ description: Configure transaction recording for token usage in LibreChat
The `transactions` object controls whether token usage records are saved to the database in LibreChat. This allows administrators to enable or disable transaction tracking independently from the balance system.
+
+
+`transactions` decides whether usage records are **written to the database**. It does not surface token counts anywhere in the app, so turning it on alone will not make token information appear in the UI.
+
+To *see* token usage, enable the in-conversation context gauge with `interface.contextUsage` and `interface.contextCost` -- see [Token Usage](/docs/configuration/token_usage#viewing-context-usage-and-cost). To enforce per-user credit limits, see [Balance](/docs/configuration/librechat_yaml/object_structure/balance).
+
+
+
**Fields under `transactions`:**
- `enabled`
diff --git a/content/docs/features/import_convos.mdx b/content/docs/features/import_convos.mdx
index 58f2d6e4e..4e0e5ad6f 100644
--- a/content/docs/features/import_convos.mdx
+++ b/content/docs/features/import_convos.mdx
@@ -10,9 +10,13 @@ Conversations Import lets you bring conversations exported from other AI chat ap
Import lives under **Settings** → **Data Controls** in LibreChat.
-## Export your conversations
+## Export from the source application
-First, export your data from the source application. The steps differ by source.
+First, export your data from the application you are migrating away from. The steps differ by source.
+
+
+See [Export from LibreChat](#export-from-librechat) at the bottom of this page.
+
@@ -83,3 +87,28 @@ Export your conversations from [ChatbotUI v1](https://github.com/mckaywrigley/ch
+
+## Export from LibreChat
+
+Export is per conversation, and lives in the **Export and Share** menu in the chat header (under the overflow menu on mobile). Open the conversation you want, choose **Export**, pick a format, and confirm.
+
+
+
+Two options in the dialog depend on the format you pick:
+
+- **Export all message branches** applies to `json` and `csv` only. Other formats export just the visible branch.
+- The remaining export options are unavailable for `csv` and `screenshot`.
+
+
+
+There is no built-in bulk export of every conversation at once, and a brand-new or search-results conversation has nothing to export, so the menu does not appear for those.
+
+
diff --git a/content/docs/features/search.mdx b/content/docs/features/search.mdx
index 39646b81d..5cd75b127 100644
--- a/content/docs/features/search.mdx
+++ b/content/docs/features/search.mdx
@@ -17,3 +17,11 @@ The integration lets users:
- Search messages and shared-link candidates with page sizes above Meilisearch's old 20-result request default
LibreChat combines conversation-index and message-index matches before loading the visible conversation page. Searches remain bounded by Meilisearch's configured `pagination.maxTotalHits`; the default ceiling used by LibreChat queries is 1,000 hits. See the [Meilisearch Configuration Guide](/docs/configuration/meilisearch) for setup, synchronization, and reindexing behavior.
+
+
+
+Conversation search is **keyword-based**. It matches the words you type, so searching for "banana" finds messages containing "banana"; it will not find a message about "fruit" that never uses the word.
+
+There is no vector or semantic search over conversation history. Semantic retrieval in LibreChat applies to **uploaded files**, not chat history: the [RAG API](/docs/features/rag_api) embeds documents you attach and retrieves them by meaning using PostgreSQL + pgvector. The two are separate systems, and enabling one does not affect the other.
+
+
From 91d9b2c30fe4c0d510a8bcecb8edeacfb4c7a62d Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:14:55 +0200
Subject: [PATCH 05/15] docs: drop leftover social-media hashtag blocks
#699 removed the pasted video description from artifacts.mdx but the same
boilerplate survived on two other feature pages: a trailing hashtag line on
agents.mdx (plus the horizontal rule left orphaned above it) and on
code_interpreter.mdx. Both had already been machine-translated into every
locale.
Feedback: 1532228462868168886
---
content/docs/features/agents.mdx | 4 ----
content/docs/features/code_interpreter.mdx | 2 --
2 files changed, 6 deletions(-)
diff --git a/content/docs/features/agents.mdx b/content/docs/features/agents.mdx
index 449c68c24..883ae4784 100644
--- a/content/docs/features/agents.mdx
+++ b/content/docs/features/agents.mdx
@@ -738,7 +738,3 @@ Using an agent, the same generation will transfer about about 52 kb of data, a 9
---
AI Agents in LibreChat provide a powerful way to create specialized assistants without coding knowledge while maintaining the flexibility to work with your preferred AI models and providers.
-
----
-
-#LibreChat #AIAssistants #NoCode #OpenSource
diff --git a/content/docs/features/code_interpreter.mdx b/content/docs/features/code_interpreter.mdx
index e7160b6ae..a47e2c613 100644
--- a/content/docs/features/code_interpreter.mdx
+++ b/content/docs/features/code_interpreter.mdx
@@ -256,5 +256,3 @@ The service runs as a set of independently scalable components (an API gateway,
The Code Interpreter API provides a secure, convenient way to execute code and manage files in an isolated sandbox. Whether you're using it through LibreChat's Agents or integrating it directly into your applications, it offers a robust solution for code execution needs.
For detailed technical specifications, deployment guides, and the API reference, see the [code-interpreter repository](https://github.com/ClickHouse/code-interpreter).
-
-#LibreChat #CodeExecution #API #Development
From 2264cb7ac1c5ef425b060d23ef9b1b042f811ca0 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:14:55 +0200
Subject: [PATCH 06/15] fix(docs): SAML_ISSUER is LibreChat's entity ID, not
Auth0's
The Auth0 SAML guide told readers to copy Auth0's Issuer into SAML_ISSUER,
and its example set it to `urn:dev-xxxxx.us.auth0.com`. That is backwards.
samlStrategy.js:315 passes SAML_ISSUER as the strategy's `issuer`, which is
the entity ID LibreChat asserts about *itself* in outbound AuthnRequests and
must match the Audience configured on the Auth0 side. The identity
provider's own issuer belongs in SAML_IDP_ISSUER, used at
samlStrategy.js:185 to verify incoming assertions.
Following the page as written produced a mismatched audience on both sides.
Also states that LibreChat publishes no SP metadata document, which is what
the reporter went looking for, and fixes three typos on the lines touched.
Feedback: 1486774904136269884
---
.../authentication/SAML/auth0.mdx | 31 +++++++++++++++----
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/content/docs/configuration/authentication/SAML/auth0.mdx b/content/docs/configuration/authentication/SAML/auth0.mdx
index ed5b80b45..71c2d9ad5 100644
--- a/content/docs/configuration/authentication/SAML/auth0.mdx
+++ b/content/docs/configuration/authentication/SAML/auth0.mdx
@@ -48,10 +48,24 @@ description: Learn how to configure LibreChat to use Auth0 for user authenticati
1. Once SAML is enabled, go back to the `SAML2 Web App` settings.
2. Go to the `Usage` tab.
-3. Click on `Identity Provider Certificate: Download Atuh0 certificate`.
-4. Use the `Issuer` to `SAML_ISSUER`
-5. Use the `Identity Provider Login URL` to `SAML_ENTRY_POINT`.
-6. Copy the donwloaded cert file to your project folder
+3. Click on `Identity Provider Certificate: Download Auth0 certificate`.
+4. Use the `Identity Provider Login URL` for `SAML_ENTRY_POINT`.
+5. Use the `Issuer` for `SAML_IDP_ISSUER` (optional, see the callout below).
+6. Copy the downloaded cert file to your project folder.
+
+
+
+`SAML_ISSUER` is the entity ID **LibreChat sends about itself** in its authentication requests, not a value you copy out of Auth0. You choose it, and it is the same string you enter on the Auth0 side as the **Audience**. Your LibreChat base URL is the conventional choice:
+
+```bash filename=".env"
+SAML_ISSUER=https://your-librechat-domain.com
+```
+
+Auth0's own `Issuer` value goes in the separate `SAML_IDP_ISSUER` variable, which LibreChat uses to check that an assertion really came from your identity provider.
+
+LibreChat does not publish a SAML metadata document, so there is no metadata URL to hand to Auth0. Configure Auth0 by hand with the Audience above and the callback URL below.
+
+

@@ -61,7 +75,12 @@ Open the `.env` file in your project folder and add the following variables:
```bash filename=".env"
SAML_ENTRY_POINT=https://dev-xxxxx.us.auth0.com/samlp/aaaaaa
- SAML_ISSUER=urn:dev-xxxxx.us.auth0.com
+
+ # Your own entity ID, sent to Auth0. Must match the Audience you set in Auth0.
+ SAML_ISSUER=https://your-librechat-domain.com
+ # Auth0's Issuer, used to verify incoming assertions (optional)
+ SAML_IDP_ISSUER=urn:dev-xxxxx.us.auth0.com
+
SAML_CERT=dev-xxxxx.pem
SAML_CALLBACK_URL=/oauth/saml/callback
SAML_SESSION_SECRET=[JustGenerateARandomSessionSecret]
@@ -74,7 +93,7 @@ Open the `.env` file in your project folder and add the following variables:
SAML_PICTURE_CLAIM=
SAML_NAME_CLAIM=
- # Logint buttion settings (optional)
+ # Login button settings (optional)
SAML_BUTTON_LABEL=
SAML_IMAGE_URL=
From 867415a827dad91aaae0dcf06810ce4ec60ab00b Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:14:56 +0200
Subject: [PATCH 07/15] docs(endpoints): explain why a custom endpoint vanishes
with no error
Seven reports say a provider never showed up after following the guide, and
the troubleshooting advice was to check the logs. That advice is misleading:
loadCustomEndpointsConfig (packages/api/src/endpoints/custom/config.ts:21-28)
filters out any endpoint missing `name`, `baseURL`, `apiKey` or `models`, or
whose `models` has neither `fetch: true` nor a non-empty `default`, and the
filter emits nothing at all. A typo or a mis-indented block therefore removes
the endpoint while the logs stay clean.
Rewrites the "Not Seeing Your Endpoint?" callout around inspecting the block
itself, notes that a duplicate name silently replaces the earlier entry, and
that a schema error anywhere in librechat.yaml stops the server rather than
disabling one section. Gives groq.mdx the enablement steps it never had:
its example uses `fetch: false`, so its `models.default` list is load-bearing
in exactly the way the filter punishes.
Also extends defaultParamsEndpoint with the four values that resolve during
schema lookup (parsers.ts:38-50) but map to no renderable parameter set.
Feedback: 1528641958187106465, 1477743384113447032, 1526344941800919159,
1477745639617073208, 1507234635573104813, 1481873299515506808
---
.../librechat_yaml/ai_endpoints/groq.mdx | 17 +++++++++++++++++
.../object_structure/custom_params.mdx | 4 ++++
content/docs/quick_start/custom_endpoints.mdx | 17 ++++++++++++++---
3 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx b/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx
index 514678fc0..abe949864 100644
--- a/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx
+++ b/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx
@@ -40,3 +40,20 @@ Add the endpoint under `endpoints.custom` in your `librechat.yaml`:
- A temperature of `0` is converted to `1e-8`. If you hit issues, use a float greater than 0 and up to 2.
- Groq is free but rate limited to 10 queries per minute and 100 per hour.
+
+## Enable the Endpoint
+
+The block above goes inside the `endpoints.custom` list of your existing `librechat.yaml`, not in a file of its own. To make it take effect:
+
+1. Add `GROQ_API_KEY` to your `.env`.
+2. Docker users: mount `librechat.yaml` into the container via `docker-compose.override.yml`. See the [custom endpoints guide](/docs/quick_start/custom_endpoints).
+3. Restart: `docker compose down && docker compose up -d`.
+4. Pick **groq** from the endpoint menu.
+
+
+
+An endpoint block missing any of `name`, `baseURL`, `apiKey`, or `models` is dropped without an error or a log line, so the provider just never appears. This example uses `fetch: false`, which means the `models.default` list is required -- remove or empty it and the endpoint disappears silently.
+
+See [Not Seeing Your Endpoint?](/docs/quick_start/custom_endpoints#step-4-restart-and-verify) for the full checklist.
+
+
diff --git a/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx b/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
index e45c6e4fd..f01dee1a9 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
@@ -34,6 +34,10 @@ Your "Google Gemini" endpoint will now display parameters for Google API when yo
]}
/>
+Note the casing: `openAI` and `azureOpenAI` are camelCase, but `openrouter` is all lowercase.
+
+The values `assistants`, `azureAssistants`, `agents`, and `bedrock` are also recognized when LibreChat resolves conversation and preset schemas, but none of them maps to a parameter set a custom endpoint can render, so setting one leaves the panel empty.
+
The field is a free-form string, not a closed enum, so a value outside this list passes configuration validation. It simply matches no parameter set, and the endpoint's panel renders with **no parameters at all** rather than reporting an error. If your parameter panel is unexpectedly empty, check this value first.
diff --git a/content/docs/quick_start/custom_endpoints.mdx b/content/docs/quick_start/custom_endpoints.mdx
index 8597153a2..88b208c84 100644
--- a/content/docs/quick_start/custom_endpoints.mdx
+++ b/content/docs/quick_start/custom_endpoints.mdx
@@ -140,15 +140,26 @@ npm run backend
Open LibreChat in your browser. Your custom endpoints should appear in the endpoint selector dropdown.
-
+
-Check the server logs for configuration errors:
+Start with the server logs:
```bash
docker compose logs api
```
-Common issues: YAML syntax errors, missing env vars, or `librechat.yaml` not mounted in Docker. Validate your YAML with the [YAML Validator](/toolkit/yaml_checker).
+**But do not stop there.** LibreChat keeps a custom endpoint only if all of `name`, `baseURL`, `apiKey`, and `models` are present, and `models` has either `fetch: true` or a non-empty `default` list. An entry failing that check is removed from the endpoint list with **no error and no log line at all** -- the provider simply never appears, and the logs look clean.
+
+So when an endpoint is missing, check the block itself before hunting through logs:
+
+- Is every one of `name`, `baseURL`, `apiKey`, `models` spelled correctly? A single typo drops the whole entry.
+- Does `models` have `fetch: true` or at least one entry under `default`?
+- Is the block indented **inside** the `endpoints.custom` list, rather than at the top level?
+- Is the `name` unique? A second endpoint with the same name (case-insensitively) silently replaces the first.
+
+Compare your block against the [Custom Endpoint Object Structure](/docs/configuration/librechat_yaml/object_structure/custom_endpoint) reference.
+
+Also note that a schema error **anywhere** in `librechat.yaml` stops the server rather than disabling one section, so one bad block elsewhere can take every custom endpoint down with it. Validate syntax with the [YAML Validator](/docs/toolkit/yaml-validator), which checks YAML syntax only, not LibreChat's schema.
From 67ec07b9e75eaf7ebeb14445381137135332290f Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:14:56 +0200
Subject: [PATCH 08/15] docs: answer the installer, API-key, session, and S3
expiry questions
/docs/local: the desktop-installer callout added by #600 only ever landed on
/docs, and /docs/local is the page the "where's the Windows installer"
report was actually filed against. It rendered as a bare install-options
grid with nothing saying LibreChat is not a downloadable app.
pre_configured_ai/openai: `user_provided` was mentioned with no explanation
of what users then do. Documents the Set API Key gear in the endpoint menu,
the expiry choices (30m/2h/12h default/1d/7d/30d/never), that the key is
encrypted per user server-side, and the revoke path.
features/authentication: no mention of session length, so "I keep getting
logged out" had nowhere to land. Adds the SESSION_EXPIRY (15m) and
REFRESH_TOKEN_EXPIRY (7d) defaults, explains that the short token renews
silently, and states plainly that there is no anonymous mode.
cdn/s3: S3_REFRESH_EXPIRY_MS was the one env var the storage code reads that
the page never listed.
Feedback: 1496108363296018513, 1488860965666816060, 1500577570784149656,
1517360828762947625, 1482888861599399978
---
content/docs/configuration/cdn/s3.mdx | 2 ++
.../docs/configuration/pre_configured_ai/openai.mdx | 10 ++++++++++
content/docs/features/authentication.mdx | 13 +++++++++++++
content/docs/local/index.mdx | 8 ++++++++
4 files changed, 33 insertions(+)
diff --git a/content/docs/configuration/cdn/s3.mdx b/content/docs/configuration/cdn/s3.mdx
index 9156efa79..bce28ce28 100644
--- a/content/docs/configuration/cdn/s3.mdx
+++ b/content/docs/configuration/cdn/s3.mdx
@@ -104,6 +104,8 @@ AWS_ENDPOINT_URL=https://your_endpoint_url
- **AWS_BUCKET_NAME:** The name of the S3 bucket you created.
- **AWS_ENDPOINT_URL:** (Optional) The custom AWS endpoint URL. Required for S3-compatible services such as MinIO, Cloudflare R2, Hetzner Object Storage, Backblaze B2, and IDrive e2. Include the URL scheme, such as `https://a7g8.da.idrivee2-32.com`; values without `http://` or `https://` can cause an `Invalid URL` error when files are streamed.
- **AWS_FORCE_PATH_STYLE:** (Optional) Set to `true` for providers that require path-style URLs (`endpoint/bucket/key`) rather than virtual-hosted-style (`bucket.endpoint/key`). Required for Hetzner Object Storage, MinIO, and similar providers whose SSL certificates don't cover bucket subdomains. Not needed for AWS S3 or Cloudflare R2. Default: `false`.
+- **S3_URL_EXPIRY_SECONDS:** (Optional) Lifetime of each presigned URL, in seconds. See the note on presigned URLs below for the provider-side caps that apply.
+- **S3_REFRESH_EXPIRY_MS:** (Optional) Regenerate a presigned URL once it reaches this age, in milliseconds, instead of using the default expiry-buffer logic. Unset by default. A value that is not a positive integer is ignored with a warning in the logs.
If you are using **IRSA** on Kubernetes, you do **not** need to set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in your environment. The AWS SDK will automatically obtain temporary credentials via the service account assigned to your pod. Ensure that `AWS_REGION` and `AWS_BUCKET_NAME` are still provided.
diff --git a/content/docs/configuration/pre_configured_ai/openai.mdx b/content/docs/configuration/pre_configured_ai/openai.mdx
index 1488b6955..547046156 100644
--- a/content/docs/configuration/pre_configured_ai/openai.mdx
+++ b/content/docs/configuration/pre_configured_ai/openai.mdx
@@ -15,6 +15,16 @@ To get your OpenAI API key, you need to:
OPENAI_API_KEY=user_provided
```
+
+
+With `user_provided`, you supply no key at all -- each user enters their own from the chat UI. Open the endpoint menu, and next to **OpenAI** there is a gear icon labelled **Set API Key**. Clicking it opens a dialog with a field for the key and a dropdown for how long it should be kept: 30 minutes, 2 hours, 12 hours (the default), 1 day, 7 days, 30 days, or never expire.
+
+The key is encrypted and stored server-side against that user's account, so it is entered once rather than per conversation, and it is never shared with other users. The same dialog has a **Revoke** action, plus **Revoke All** to clear every key that user has stored.
+
+Until a user sets a key, the endpoint is visible but unusable for them.
+
+
+
- You can determine which models you would like to have available with `OPENAI_MODELS`
- When `OPENAI_API_KEY` is set to `user_provided` → only the models put in this list will be available
- ⚠️New models won't automatically show up; you'll need to add them to this list first
diff --git a/content/docs/features/authentication.mdx b/content/docs/features/authentication.mdx
index 8235bd315..a22491f07 100644
--- a/content/docs/features/authentication.mdx
+++ b/content/docs/features/authentication.mdx
@@ -20,6 +20,19 @@ Additionally, our system can integrate social logins from various platforms such
**See also:** [Access Control](/docs/features/access_control), LibreChat's granular permission system for users, groups, and roles, covering per-resource sharing of agents, prompts, MCP servers, and feature-level permissions.
+## Staying Signed In
+
+An account is always required. LibreChat has no anonymous or guest mode, so there is no way to use it without logging in.
+
+Two settings decide how long a session lasts, and both are configurable:
+
+- `SESSION_EXPIRY` -- how long an access token stays valid. Defaults to **15 minutes**.
+- `REFRESH_TOKEN_EXPIRY` -- how long you stay signed in overall. Defaults to **7 days**.
+
+The short access token is renewed automatically in the background while you are using LibreChat, so the 15-minute figure is not how often you are asked to log in again. Being signed out usually means the refresh token reached the end of its window, or the browser dropped the refresh cookie.
+
+If you are being logged out sooner than expected, raise `REFRESH_TOKEN_EXPIRY`. Both variables are documented in the [.env reference](/docs/configuration/dotenv).
+
+
+
+
+LibreChat is a self-hosted web application, not a native Windows app or Linux AppImage. There is no installer to download and run: you start the server using one of the options above, then use LibreChat in your browser.
+
+On Windows, [Docker Desktop](/docs/local/docker) is the simplest route.
+
+
From 15eeb3a5d559e69af57d06bddf52c0df8a5151d1 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Thu, 3 Sep 2026 00:22:30 +0200
Subject: [PATCH 09/15] docs(cdn): say what a file storage provider is actually
used for
The Azure page described the service but never what LibreChat stores in it,
which was the reported complaint. Storage strategy is not a feature toggle:
it decides where avatars, chat uploads, image-generation output, and Code
Interpreter files all go. Names those consumers and the reason to move off
the default local disk, which does not survive container recreation and
cannot be shared across replicas.
Feedback: 1512086950859767868
---
content/docs/configuration/cdn/azure.mdx | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/content/docs/configuration/cdn/azure.mdx b/content/docs/configuration/cdn/azure.mdx
index 9d0fdf987..71003cd1f 100644
--- a/content/docs/configuration/cdn/azure.mdx
+++ b/content/docs/configuration/cdn/azure.mdx
@@ -17,6 +17,12 @@ description: This document provides instructions for setting up Azure Blob Stora
Azure Blob Storage offers scalable, secure object storage for files in LibreChat. Follow these steps to configure your Azure Blob Storage.
+### What uses this
+
+Setting a file strategy changes where LibreChat puts every file it stores, rather than enabling a particular feature. That covers user and agent avatars, files uploaded in chat, images returned by the image generation tools, and files produced by the Code Interpreter.
+
+The default strategy writes all of that to the container's local disk, which disappears when the container is recreated and cannot be shared between replicas. Configure Azure Blob Storage (or another provider under [CDN](/docs/configuration/cdn)) when you need those files to outlive a redeploy or to be reachable from more than one instance.
+
## 1. Create an Azure Storage Account
1. **Sign in to Azure:**
From 786a9ecaebc2251e74f6c7147e4c333c435d68ff Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Fri, 4 Sep 2026 01:21:22 +0200
Subject: [PATCH 10/15] docs(speech): correct autoSendText units and
autoTranscribeAudio behavior
autoSendText is seconds, not milliseconds, and -1 is what disables
auto-send; 0 sends immediately. autoTranscribeAudio controls continuous
listening (and silence detection on external engines), not transcription
of audio messages. The reference table and the STT/TTS guide disagreed
with each other and both disagreed with the client.
Also note that privately resolved cloud endpoints are not exempt from the
SSRF guard, and that browser TTS voices come from the OS rather than a
configured voice list.
---
.../librechat_yaml/object_structure/speech.mdx | 6 +++---
content/docs/configuration/stt_tts.mdx | 10 +++++-----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/content/docs/configuration/librechat_yaml/object_structure/speech.mdx b/content/docs/configuration/librechat_yaml/object_structure/speech.mdx
index 36e49391f..9ad9eb4a6 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/speech.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/speech.mdx
@@ -272,9 +272,9 @@ When using an object instead of a boolean:
'browser',
],
['languageSTT', 'String', 'Default language for STT.', ''],
- ['autoTranscribeAudio', 'Boolean', 'Automatically transcribe audio messages.', ''],
- ['decibelValue', 'Number', 'Decibel threshold for voice detection.', ''],
- ['autoSendText', 'Number', 'Delay in ms before auto-sending transcribed text (0 to disable).', ''],
+ ['autoTranscribeAudio', 'Boolean', 'Keep the microphone listening instead of stopping at the first pause. With an external engine it also turns on silence detection based on `decibelValue`.', ''],
+ ['decibelValue', 'Number', 'Decibel threshold for silence detection. Range -100 to -30, default -45.', ''],
+ ['autoSendText', 'Number', 'Seconds to wait after transcription before auto-sending. `0` sends immediately; `-1` disables auto-send.', ''],
]}
/>
diff --git a/content/docs/configuration/stt_tts.mdx b/content/docs/configuration/stt_tts.mdx
index 1e5b96343..a6ea4336d 100644
--- a/content/docs/configuration/stt_tts.mdx
+++ b/content/docs/configuration/stt_tts.mdx
@@ -42,7 +42,7 @@ speech:
# ...
```
-Entries are bare `host:port` pairs -- no scheme or path, port required, IPv6 bracketed as `[::1]:8080`, and IP literals must be private. Cloud providers such as OpenAI, Azure, and ElevenLabs need no entry. The same field and rules apply under `speech.stt`. See [SSRF protection](/docs/configuration/librechat_yaml/object_structure/web_search#ssrf-protection-and-private-providers) for the full entry format.
+Entries are bare `host:port` pairs: no scheme or path, port required, IPv6 bracketed as `[::1]:8080`, and IP literals must be private. Public cloud endpoints such as OpenAI, Azure, and ElevenLabs need no entry. The guard works on the resolved IP, not the hostname, so a cloud endpoint reached over Private Link, private DNS, or a VPN resolves into private address space and does need its exact `host:port` listed like any other private target. The same field and rules apply under `speech.stt`. See [SSRF protection](/docs/configuration/librechat_yaml/object_structure/web_search#ssrf-protection-and-private-providers) for the full entry format.
## Speech Tab (optional)
@@ -91,8 +91,8 @@ speech:
options={[
['engineSTT', 'String', 'Which transcription engine to use. `browser` uses the built-in Web Speech API and needs no server config; the others use the matching provider block under `speech.stt`.', 'Options: "browser", "external", "openai", "azureOpenAI"'],
['languageSTT', 'String', 'Language the transcriber should expect, as shown in the speech settings dropdown.', 'Example: "English (US)"'],
- ['autoTranscribeAudio', 'Boolean', 'Start transcribing as soon as speech is detected, instead of requiring a button press.', ''],
- ['decibelValue', 'Number', 'Silence threshold in dB used by auto-transcribe to decide when you have stopped speaking. Lower values are more sensitive to quiet speech.', 'Range: -100 to -30. Default: -45'],
+ ['autoTranscribeAudio', 'Boolean', 'Keep the microphone listening instead of stopping at the first pause. With an external engine it also turns on silence detection, which uses `decibelValue` to decide when you have stopped speaking and ends the recording. You still start the recording yourself.', ''],
+ ['decibelValue', 'Number', 'Silence threshold in dB used by that silence detection. Lower values are more sensitive to quiet speech.', 'Range: -100 to -30. Default: -45'],
['autoSendText', 'Number', 'Seconds to wait after transcription finishes before sending the message automatically. `0` sends immediately; `-1` disables auto-send.', 'Range: 0 to 60, or -1'],
]}
/>
@@ -102,7 +102,7 @@ speech:
-`engineSTT` / `engineTTS` only choose which engine the UI starts on. Anything other than `browser` still needs the corresponding provider configured under `speech.stt` or `speech.tts` -- see the sections below.
+`engineSTT` / `engineTTS` only choose which engine the UI starts on. Anything other than `browser` still needs the corresponding provider configured under `speech.stt` or `speech.tts`. See the sections below.
From d35a95a80707e2655f900d0ef17c0f76f7805c02 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Fri, 4 Sep 2026 01:21:30 +0200
Subject: [PATCH 11/15] docs: address review feedback on endpoint, banner, and
auth pages
- Drop the Groq enable-the-endpoint section; the custom endpoints guide
already covers it.
- contextUsage draws the gauge and is on by default; contextCost only
adds pricing and is off by default. Don't present both as required.
- An account is required to chat, but ALLOW_SHARED_LINKS_PUBLIC lets
anonymous visitors read a shared conversation.
- defaultParamsEndpoint is filled in from provider when omitted, so
'custom' is not the effective default for a provider-backed endpoint.
- Warn that OLLAMA_HOST=0.0.0.0 exposes an unauthenticated API on every
interface, and explain that apiKey is only the Bearer fallback.
- Replace double hyphens used as pauses.
---
content/docs/configuration/banner.mdx | 4 ++--
.../librechat_yaml/ai_endpoints/groq.mdx | 17 -----------------
.../librechat_yaml/ai_endpoints/ollama.mdx | 19 ++++++++++++++++++-
.../object_structure/custom_params.mdx | 8 +++++++-
.../object_structure/transactions.mdx | 2 +-
.../pre_configured_ai/openai.mdx | 2 +-
content/docs/features/authentication.mdx | 6 +++---
content/docs/quick_start/custom_endpoints.mdx | 2 +-
8 files changed, 33 insertions(+), 27 deletions(-)
diff --git a/content/docs/configuration/banner.mdx b/content/docs/configuration/banner.mdx
index e155708ee..75d011973 100644
--- a/content/docs/configuration/banner.mdx
+++ b/content/docs/configuration/banner.mdx
@@ -54,7 +54,7 @@ Is persistable (cannot be dismissed) (y/N):
-There is only ever one banner. `update-banner` looks for an existing banner and overwrites it in place, creating one only when none exists -- so you cannot queue up a second banner for a later date, and you cannot have two scheduled windows overlap. Running the command again discards the previous banner's message and schedule.
+There is only ever one banner. `update-banner` looks for an existing banner and overwrites it in place, creating one only when none exists, so you cannot queue up a second banner for a later date, and you cannot have two scheduled windows overlap. Running the command again discards the previous banner's message and schedule.
To change a banner, run `update-banner` again. To take one down before its `Display To` time, use `delete-banner` below.
@@ -70,7 +70,7 @@ npm run delete-banner
You'll see the current banner and be asked to confirm before deleting.
-Removing a banner this way is also how you take one down early -- there is no "disable" flag. A banner otherwise disappears on its own once `Display To` passes, and one saved with no `Display To` stays up until you delete it.
+Removing a banner this way is also how you take one down early: there is no "disable" flag. A banner otherwise disappears on its own once `Display To` passes, and one saved with no `Display To` stays up until you delete it.
---
diff --git a/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx b/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx
index abe949864..514678fc0 100644
--- a/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx
+++ b/content/docs/configuration/librechat_yaml/ai_endpoints/groq.mdx
@@ -40,20 +40,3 @@ Add the endpoint under `endpoints.custom` in your `librechat.yaml`:
- A temperature of `0` is converted to `1e-8`. If you hit issues, use a float greater than 0 and up to 2.
- Groq is free but rate limited to 10 queries per minute and 100 per hour.
-
-## Enable the Endpoint
-
-The block above goes inside the `endpoints.custom` list of your existing `librechat.yaml`, not in a file of its own. To make it take effect:
-
-1. Add `GROQ_API_KEY` to your `.env`.
-2. Docker users: mount `librechat.yaml` into the container via `docker-compose.override.yml`. See the [custom endpoints guide](/docs/quick_start/custom_endpoints).
-3. Restart: `docker compose down && docker compose up -d`.
-4. Pick **groq** from the endpoint menu.
-
-
-
-An endpoint block missing any of `name`, `baseURL`, `apiKey`, or `models` is dropped without an error or a log line, so the provider just never appears. This example uses `fetch: false`, which means the `models.default` list is required -- remove or empty it and the endpoint disappears silently.
-
-See [Not Seeing Your Endpoint?](/docs/quick_start/custom_endpoints#step-4-restart-and-verify) for the full checklist.
-
-
diff --git a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
index 78f7791e4..1ae851db3 100644
--- a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
+++ b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
@@ -98,10 +98,27 @@ Work through these in order:
1. **Check the endpoint is reachable from LibreChat, not from your shell.** If LibreChat runs in Docker, `localhost` is the API container itself, not your host. Use `http://host.docker.internal:11434/v1/` on Docker Desktop, or the host's LAN address on Linux where `host.docker.internal` may be unavailable. Running LibreChat outside Docker is the only case where `http://localhost:11434/v1/` is correct.
2. **Confirm Ollama is listening beyond loopback.** By default Ollama binds to `127.0.0.1`, which a container cannot reach. Set `OLLAMA_HOST=0.0.0.0` in Ollama's own environment and restart it.
+
+
+ Ollama's API is unauthenticated. Binding it to `0.0.0.0` on a machine with a LAN or public interface hands model access, and the ability to pull and delete models, to anyone who can reach port 11434. Prefer binding to just the address the LibreChat container actually reaches, which is the gateway of its compose network (`docker network inspect ` reports it), and firewall port 11434 so nothing else can reach it.
+
3. **Keep the endpoint named `Ollama`.** Model fetching has an Ollama-specific path that is selected by name: LibreChat only queries Ollama's `/api/tags` when the endpoint `name` starts with `ollama`, case-insensitively. Rename it to something else and `fetch: true` falls back to the generic OpenAI-compatible `/models` call, which Ollama does not serve the same way, so the list comes back empty.
4. **Set `apiKey` to any non-empty placeholder.** Ollama ignores the value, but a custom endpoint with no `apiKey` is dropped at config load.
5. **Read the API logs.** `docker compose logs api` reports the connection error and the URL it actually tried.
### Using a remote or hosted Ollama server
-Nothing is local-specific except the URL: point `baseURL` at the remote server's OpenAI-compatible path and supply whatever credential that server expects in `apiKey` instead of the placeholder. Keep the endpoint name starting with `ollama` if you want model fetching to keep working.
+Nothing is local-specific except the URL: point `baseURL` at the remote server's OpenAI-compatible path and put the credential in `apiKey` instead of the placeholder. Keep the endpoint name starting with `ollama` if you want model fetching to keep working.
+
+`apiKey` is only ever sent as `Authorization: Bearer `, and only when your `headers` block has not already set an `Authorization` header. If your hosted proxy expects a different scheme, such as `X-API-Key` or Basic auth, put the real credential in `headers`: an `Authorization` entry there replaces the Bearer fallback, and any other header is sent alongside it. `apiKey` still has to be non-empty either way, because an endpoint without one is dropped at config load.
+
+```yaml filename="excerpt of librechat.yaml"
+- name: "Ollama"
+ apiKey: "unused"
+ baseURL: "https://ollama.example.com/v1/"
+ headers:
+ X-API-Key: "${OLLAMA_PROXY_KEY}"
+ models:
+ default: ["llama3:latest"]
+ fetch: false
+```
diff --git a/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx b/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
index f01dee1a9..e3250f6ff 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
@@ -25,7 +25,7 @@ Your "Google Gemini" endpoint will now display parameters for Google API when yo
+
+If the custom endpoint sets `provider`, LibreChat fills `defaultParamsEndpoint` in from it, so the effective default is the provider's parameter set rather than `custom`. That substitution happens only when you leave the field out or leave it at `custom`; any other value you set explicitly wins. A `provider: 'anthropic'` endpoint therefore starts on the Anthropic parameter set without you writing `defaultParamsEndpoint` at all.
+
+
+
The values `assistants`, `azureAssistants`, `agents`, and `bedrock` are also recognized when LibreChat resolves conversation and preset schemas, but none of them maps to a parameter set a custom endpoint can render, so setting one leaves the panel empty.
diff --git a/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx b/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx
index ffccc5671..4379dc4e9 100644
--- a/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx
+++ b/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx
@@ -12,7 +12,7 @@ The `transactions` object controls whether token usage records are saved to the
`transactions` decides whether usage records are **written to the database**. It does not surface token counts anywhere in the app, so turning it on alone will not make token information appear in the UI.
-To *see* token usage, enable the in-conversation context gauge with `interface.contextUsage` and `interface.contextCost` -- see [Token Usage](/docs/configuration/token_usage#viewing-context-usage-and-cost). To enforce per-user credit limits, see [Balance](/docs/configuration/librechat_yaml/object_structure/balance).
+To *see* token usage, use `interface.contextUsage`, which draws the in-conversation context gauge and is on by default. `interface.contextCost` is a separate setting, off by default, that adds cost figures to that gauge; leave it off if you want token counts without exposing pricing. See [Token Usage](/docs/configuration/token_usage#viewing-context-usage-and-cost). To enforce per-user credit limits, see [Balance](/docs/configuration/librechat_yaml/object_structure/balance).
diff --git a/content/docs/configuration/pre_configured_ai/openai.mdx b/content/docs/configuration/pre_configured_ai/openai.mdx
index 547046156..9b3b44e37 100644
--- a/content/docs/configuration/pre_configured_ai/openai.mdx
+++ b/content/docs/configuration/pre_configured_ai/openai.mdx
@@ -17,7 +17,7 @@ OPENAI_API_KEY=user_provided
-With `user_provided`, you supply no key at all -- each user enters their own from the chat UI. Open the endpoint menu, and next to **OpenAI** there is a gear icon labelled **Set API Key**. Clicking it opens a dialog with a field for the key and a dropdown for how long it should be kept: 30 minutes, 2 hours, 12 hours (the default), 1 day, 7 days, 30 days, or never expire.
+With `user_provided`, you supply no key at all: each user enters their own from the chat UI. Open the endpoint menu, and next to **OpenAI** there is a gear icon labelled **Set API Key**. Clicking it opens a dialog with a field for the key and a dropdown for how long it should be kept: 30 minutes, 2 hours, 12 hours (the default), 1 day, 7 days, 30 days, or never expire.
The key is encrypted and stored server-side against that user's account, so it is entered once rather than per conversation, and it is never shared with other users. The same dialog has a **Revoke** action, plus **Revoke All** to clear every key that user has stored.
diff --git a/content/docs/features/authentication.mdx b/content/docs/features/authentication.mdx
index a22491f07..284025331 100644
--- a/content/docs/features/authentication.mdx
+++ b/content/docs/features/authentication.mdx
@@ -22,12 +22,12 @@ Additionally, our system can integrate social logins from various platforms such
## Staying Signed In
-An account is always required. LibreChat has no anonymous or guest mode, so there is no way to use it without logging in.
+Using LibreChat requires an account. There is no anonymous or guest mode for chatting, so you cannot start a conversation without logging in. The one exception is viewing: when an admin sets `ALLOW_SHARED_LINKS_PUBLIC=true`, anyone holding a [shared link](/docs/features/shareable_links) can read that conversation without an account. They can only read it.
Two settings decide how long a session lasts, and both are configurable:
-- `SESSION_EXPIRY` -- how long an access token stays valid. Defaults to **15 minutes**.
-- `REFRESH_TOKEN_EXPIRY` -- how long you stay signed in overall. Defaults to **7 days**.
+- `SESSION_EXPIRY`: how long an access token stays valid. Defaults to **15 minutes**.
+- `REFRESH_TOKEN_EXPIRY`: how long you stay signed in overall. Defaults to **7 days**.
The short access token is renewed automatically in the background while you are using LibreChat, so the 15-minute figure is not how often you are asked to log in again. Being signed out usually means the refresh token reached the end of its window, or the browser dropped the refresh cookie.
diff --git a/content/docs/quick_start/custom_endpoints.mdx b/content/docs/quick_start/custom_endpoints.mdx
index 88b208c84..ddfc39252 100644
--- a/content/docs/quick_start/custom_endpoints.mdx
+++ b/content/docs/quick_start/custom_endpoints.mdx
@@ -148,7 +148,7 @@ Start with the server logs:
docker compose logs api
```
-**But do not stop there.** LibreChat keeps a custom endpoint only if all of `name`, `baseURL`, `apiKey`, and `models` are present, and `models` has either `fetch: true` or a non-empty `default` list. An entry failing that check is removed from the endpoint list with **no error and no log line at all** -- the provider simply never appears, and the logs look clean.
+**But do not stop there.** LibreChat keeps a custom endpoint only if all of `name`, `baseURL`, `apiKey`, and `models` are present, and `models` has either `fetch: true` or a non-empty `default` list. An entry failing that check is removed from the endpoint list with **no error and no log line at all**: the provider simply never appears, and the logs look clean.
So when an endpoint is missing, check the block itself before hunting through logs:
From 3cfa88395986e6413db56d7a0405d30ce075e593 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Fri, 4 Sep 2026 01:43:40 +0200
Subject: [PATCH 12/15] docs: correct storage persistence, Ollama model fetch,
and key entry paths
- Compose bind-mounts ./images and ./uploads to the host, so local file
storage is not lost when the API container is recreated. Scope the
warning to multi-instance and no-persistent-volume deployments.
- A renamed Ollama endpoint does not come back with an empty model list.
The native /api/tags call is tried only for names starting with ollama,
but any other name, or a failure, falls through to /v1/models, which
Ollama serves at the documented /v1/ base URL.
- Auth0's Settings JSON needs an audience matching SAML_ISSUER, otherwise
Auth0 asserts its default audience and login fails on a mismatch.
- Provider keys can also be set from Settings, Data controls, API keys,
which is the only path when a model spec hides the endpoint menu.
---
content/docs/configuration/authentication/SAML/auth0.mdx | 6 ++++++
content/docs/configuration/cdn/azure.mdx | 2 +-
.../configuration/librechat_yaml/ai_endpoints/ollama.mdx | 4 ++--
content/docs/configuration/pre_configured_ai/openai.mdx | 2 ++
4 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/content/docs/configuration/authentication/SAML/auth0.mdx b/content/docs/configuration/authentication/SAML/auth0.mdx
index 71c2d9ad5..8383f06d3 100644
--- a/content/docs/configuration/authentication/SAML/auth0.mdx
+++ b/content/docs/configuration/authentication/SAML/auth0.mdx
@@ -27,12 +27,18 @@ description: Learn how to configure LibreChat to use Auth0 for user authenticati
- **Settings (JSON Format)**: Use the following configuration:
```json
{
+ "audience": "https://your-librechat-domain.com",
"mappings": {
"email": "email",
"name": "username"
}
}
```
+
+ Set `audience` to the same string you will use for `SAML_ISSUER` in Step 4.
+ Leave it out and Auth0 asserts its own default audience, which will not match
+ what LibreChat sends, and the login fails with an audience mismatch.
+
If your application requires additional attributes such as `given_name`,
`family_name`, `username` or `picture`, ensure these mappings are properly
diff --git a/content/docs/configuration/cdn/azure.mdx b/content/docs/configuration/cdn/azure.mdx
index 71003cd1f..77b70b61e 100644
--- a/content/docs/configuration/cdn/azure.mdx
+++ b/content/docs/configuration/cdn/azure.mdx
@@ -21,7 +21,7 @@ Azure Blob Storage offers scalable, secure object storage for files in LibreChat
Setting a file strategy changes where LibreChat puts every file it stores, rather than enabling a particular feature. That covers user and agent avatars, files uploaded in chat, images returned by the image generation tools, and files produced by the Code Interpreter.
-The default strategy writes all of that to the container's local disk, which disappears when the container is recreated and cannot be shared between replicas. Configure Azure Blob Storage (or another provider under [CDN](/docs/configuration/cdn)) when you need those files to outlive a redeploy or to be reachable from more than one instance.
+The default strategy writes all of that to the API container's filesystem. The standard Docker Compose deployment bind-mounts `./images` and `./uploads` to the host, so on a single-instance compose setup those files already survive recreating the container. What local storage cannot do is share files across instances, and it does lose them on any deployment that leaves those paths on the container's writable layer, such as Kubernetes without a persistent volume. Configure Azure Blob Storage (or another provider under [CDN](/docs/configuration/cdn)) when you run more than one instance, or when nothing persistent is mounted behind those paths.
## 1. Create an Azure Storage Account
diff --git a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
index 1ae851db3..05175b188 100644
--- a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
+++ b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
@@ -102,13 +102,13 @@ Work through these in order:
Ollama's API is unauthenticated. Binding it to `0.0.0.0` on a machine with a LAN or public interface hands model access, and the ability to pull and delete models, to anyone who can reach port 11434. Prefer binding to just the address the LibreChat container actually reaches, which is the gateway of its compose network (`docker network inspect ` reports it), and firewall port 11434 so nothing else can reach it.
-3. **Keep the endpoint named `Ollama`.** Model fetching has an Ollama-specific path that is selected by name: LibreChat only queries Ollama's `/api/tags` when the endpoint `name` starts with `ollama`, case-insensitively. Rename it to something else and `fetch: true` falls back to the generic OpenAI-compatible `/models` call, which Ollama does not serve the same way, so the list comes back empty.
+3. **Know what the endpoint name changes.** LibreChat reaches for Ollama's native `/api/tags` only when the endpoint `name` starts with `ollama`, case-insensitively. Any other name, or a failure of that native call, falls through to the generic OpenAI-compatible `/v1/models` request. Current Ollama versions answer that one too at the `/v1/` base URL above, so a renamed endpoint usually still returns a model list. The prefix matters when you specifically need the native tags route, and a hosted proxy that serves `/v1/models` but not `/api/tags` is better off without it.
4. **Set `apiKey` to any non-empty placeholder.** Ollama ignores the value, but a custom endpoint with no `apiKey` is dropped at config load.
5. **Read the API logs.** `docker compose logs api` reports the connection error and the URL it actually tried.
### Using a remote or hosted Ollama server
-Nothing is local-specific except the URL: point `baseURL` at the remote server's OpenAI-compatible path and put the credential in `apiKey` instead of the placeholder. Keep the endpoint name starting with `ollama` if you want model fetching to keep working.
+Nothing is local-specific except the URL: point `baseURL` at the remote server's OpenAI-compatible path and put the credential in `apiKey` instead of the placeholder. Name it whatever you like: model fetching falls back to the OpenAI-compatible `/v1/models` route, which is usually what a hosted proxy exposes.
`apiKey` is only ever sent as `Authorization: Bearer `, and only when your `headers` block has not already set an `Authorization` header. If your hosted proxy expects a different scheme, such as `X-API-Key` or Basic auth, put the real credential in `headers`: an `Authorization` entry there replaces the Bearer fallback, and any other header is sent alongside it. `apiKey` still has to be non-empty either way, because an endpoint without one is dropped at config load.
diff --git a/content/docs/configuration/pre_configured_ai/openai.mdx b/content/docs/configuration/pre_configured_ai/openai.mdx
index 9b3b44e37..30ae55811 100644
--- a/content/docs/configuration/pre_configured_ai/openai.mdx
+++ b/content/docs/configuration/pre_configured_ai/openai.mdx
@@ -21,6 +21,8 @@ With `user_provided`, you supply no key at all: each user enters their own from
The key is encrypted and stored server-side against that user's account, so it is entered once rather than per conversation, and it is never shared with other users. The same dialog has a **Revoke** action, plus **Revoke All** to clear every key that user has stored.
+If the endpoint menu is hidden, for example because a model spec disables `modelSelect`, the same dialog is reachable from **Settings** under **Data controls**, in the **API keys** section.
+
Until a user sets a key, the endpoint is visible but unusable for them.
From 722c276777f172b1b18a301e42726d057c38a3c5 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Fri, 4 Sep 2026 02:08:54 +0200
Subject: [PATCH 13/15] fix(docs): canonicalize the object_structure folder
link for every locale
The next.config redirect only covers the unprefixed URL. Translated
index pages still carry the nav-only folder as the Object Structure card
target, and TrackedLink prefixes it with the active locale, so the card
stayed a 404 for all thirteen non-English readers.
Canonicalize the path in localize-href alongside the legacy /toolkit
aliases, which happens before locale prefixing and so fixes every locale
at render time. Verified against a production server: no locale emits
the bare folder href any more.
Also note that a refresh inherits the session's original expiry rather
than restarting it, so REFRESH_TOKEN_EXPIRY is measured from login.
---
content/docs/features/authentication.mdx | 2 ++
lib/localize-href.test.ts | 9 +++++++++
lib/localize-href.ts | 14 ++++++++++----
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/content/docs/features/authentication.mdx b/content/docs/features/authentication.mdx
index 284025331..25a23e1a5 100644
--- a/content/docs/features/authentication.mdx
+++ b/content/docs/features/authentication.mdx
@@ -31,6 +31,8 @@ Two settings decide how long a session lasts, and both are configurable:
The short access token is renewed automatically in the background while you are using LibreChat, so the 15-minute figure is not how often you are asked to log in again. Being signed out usually means the refresh token reached the end of its window, or the browser dropped the refresh cookie.
+Each renewal does hand back a new refresh token, but it is signed against the same session and inherits that session's original expiry. `REFRESH_TOKEN_EXPIRY` is therefore measured from when you logged in, not from your last activity: staying active does not extend it, and you are signed out when the window runs out.
+
If you are being logged out sooner than expected, raise `REFRESH_TOKEN_EXPIRY`. Both variables are documented in the [.env reference](/docs/configuration/dotenv).
diff --git a/lib/localize-href.test.ts b/lib/localize-href.test.ts
index a327ac020..45002384f 100644
--- a/lib/localize-href.test.ts
+++ b/lib/localize-href.test.ts
@@ -22,6 +22,15 @@ describe('localizeDocsHref', () => {
)
})
+ it('canonicalizes the nav-only object_structure folder in every locale', () => {
+ expect(localizeDocsHref('/docs/configuration/librechat_yaml/object_structure', '/docs/x')).toBe(
+ '/docs/configuration/librechat_yaml/object_structure/config',
+ )
+ expect(
+ localizeDocsHref('/docs/configuration/librechat_yaml/object_structure', '/fr/docs/x'),
+ ).toBe('/fr/docs/configuration/librechat_yaml/object_structure/config')
+ })
+
it('does not touch external links, anchors, non-docs paths, or raw markdown', () => {
expect(localizeDocsHref('https://example.com/docs/x', '/de/docs/x')).toBe(
'https://example.com/docs/x',
diff --git a/lib/localize-href.ts b/lib/localize-href.ts
index cf368f014..456a00941 100644
--- a/lib/localize-href.ts
+++ b/lib/localize-href.ts
@@ -4,25 +4,31 @@ export function isExternalHref(href: string): boolean {
return href.startsWith('http') || href.startsWith('mailto:')
}
-const LEGACY_TOOLKIT_HREFS: Record = {
+const LEGACY_DOCS_HREFS: Record = {
'/toolkit': '/docs/toolkit',
'/toolkit/creds-generator': '/docs/toolkit/credentials-generator',
'/toolkit/creds_generator': '/docs/toolkit/credentials-generator',
'/toolkit/yaml-checker': '/docs/toolkit/yaml-validator',
'/toolkit/yaml_checker': '/docs/toolkit/yaml-validator',
+ // Nav-only folder with no page of its own; the section's reference is Config
+ // Structure. Canonicalized here as well as in next.config.mjs because the
+ // redirect only covers the unprefixed URL, while translated pages still carry
+ // the folder target and get a locale prefix applied below.
+ '/docs/configuration/librechat_yaml/object_structure':
+ '/docs/configuration/librechat_yaml/object_structure/config',
}
function canonicalizeDocsHref(href: string): string {
const match = href.match(/^([^?#]*)([?#].*)?$/)
const path = match?.[1] ?? href
const suffix = match?.[2] ?? ''
- const canonicalPath = LEGACY_TOOLKIT_HREFS[path]
+ const canonicalPath = LEGACY_DOCS_HREFS[path]
return canonicalPath ? `${canonicalPath}${suffix}` : href
}
/**
- * Prefix an internal `/docs` page link, including legacy `/toolkit` aliases,
- * with the active non-default locale (derived from the current pathname's first
+ * Prefix an internal `/docs` page link, including legacy `/toolkit` aliases and
+ * the nav-only object_structure folder, with the active non-default locale (derived from the current pathname's first
* segment) so a reader on `//docs/...` stays in that locale when
* following related-guide links and cards.
*
From c12abdf58f701dd788782ba533827e54a132d7e9 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Fri, 4 Sep 2026 02:26:50 +0200
Subject: [PATCH 14/15] docs(auth): scope session expiry to LibreChat-issued
refresh tokens
With OPENID_REUSE_TOKENS=true the refresh cookie holds the provider's
token, so the IdP's lifetime, rotation, and revocation policy end the
session and raising REFRESH_TOKEN_EXPIRY cannot extend it.
---
content/docs/features/authentication.mdx | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/content/docs/features/authentication.mdx b/content/docs/features/authentication.mdx
index 25a23e1a5..16a122f46 100644
--- a/content/docs/features/authentication.mdx
+++ b/content/docs/features/authentication.mdx
@@ -35,6 +35,12 @@ Each renewal does hand back a new refresh token, but it is signed against the sa
If you are being logged out sooner than expected, raise `REFRESH_TOKEN_EXPIRY`. Both variables are documented in the [.env reference](/docs/configuration/dotenv).
+
+
+Everything above describes refresh tokens that LibreChat issues itself. With [`OPENID_REUSE_TOKENS=true`](/docs/configuration/authentication/OAuth2-OIDC/token-reuse), the cookie holds your OpenID provider's refresh token instead, so that provider's lifetime, rotation, and revocation policy decide when the session ends. `REFRESH_TOKEN_EXPIRY` does not extend an IdP credential that has expired or been revoked; change the session policy at the provider.
+
+
+
Date: Fri, 4 Sep 2026 02:53:04 +0200
Subject: [PATCH 15/15] docs(ollama): correct the stale 'fetching not
supported' comments
The page's primary example already uses fetch: true, and models.ts has a
dedicated Ollama fetch path, so the two examples in the stop-sequence
callout contradicted the rest of the page. Say why the list is pinned
instead, and let the hosted-proxy example fetch, which is the point of
the /v1/models fallback described above it.
---
.../configuration/librechat_yaml/ai_endpoints/ollama.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
index 05175b188..4704a8924 100644
--- a/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
+++ b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
@@ -52,7 +52,7 @@ If `llama3` keeps generating without stopping, add an `addParams` block with the
default: [
"llama3"
]
- fetch: false # fetching list of models is not supported
+ fetch: false # pinned to the list above; set true to discover models from the server
titleConvo: true
titleModel: "current_model"
summarize: false
@@ -78,7 +78,7 @@ If you only run `llama3` with Ollama, setting `stop` at the config level via `ad
"llama3:latest",
"mistral"
]
- fetch: false # fetching list of models is not supported
+ fetch: false # pinned to the list above; set true to discover models from the server
titleConvo: true
titleModel: "current_model"
modelDisplayLabel: "Ollama"
@@ -120,5 +120,5 @@ Nothing is local-specific except the URL: point `baseURL` at the remote server's
X-API-Key: "${OLLAMA_PROXY_KEY}"
models:
default: ["llama3:latest"]
- fetch: false
+ fetch: true
```