diff --git a/content/docs/configuration/authentication/SAML/auth0.mdx b/content/docs/configuration/authentication/SAML/auth0.mdx
index ed5b80b45..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
@@ -48,10 +54,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 +81,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 +99,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=
diff --git a/content/docs/configuration/banner.mdx b/content/docs/configuration/banner.mdx
index 11ac5287c..75d011973 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/cdn/azure.mdx b/content/docs/configuration/cdn/azure.mdx
index 9d0fdf987..77b70b61e 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 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
1. **Sign in to Azure:**
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/librechat_yaml/ai_endpoints/ollama.mdx b/content/docs/configuration/librechat_yaml/ai_endpoints/ollama.mdx
index 5c5923935..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,14 +78,47 @@ 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"
```
-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.
+
+
+ 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. **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. 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.
+
+```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: true
+```
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/configuration/librechat_yaml/object_structure/custom_params.mdx b/content/docs/configuration/librechat_yaml/object_structure/custom_params.mdx
index 764594a28..e3250f6ff 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,37 @@ 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:
+
+
+
+Note the casing: `openAI` and `azureOpenAI` are camelCase, but `openrouter` is all lowercase.
+
+
+
+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.
+
+
+
+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/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/librechat_yaml/object_structure/transactions.mdx b/content/docs/configuration/librechat_yaml/object_structure/transactions.mdx
index d63908200..4379dc4e9 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, 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).
+
+
+
**Fields under `transactions`:**
- `enabled`
diff --git a/content/docs/configuration/pre_configured_ai/openai.mdx b/content/docs/configuration/pre_configured_ai/openai.mdx
index 1488b6955..30ae55811 100644
--- a/content/docs/configuration/pre_configured_ai/openai.mdx
+++ b/content/docs/configuration/pre_configured_ai/openai.mdx
@@ -15,6 +15,18 @@ 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.
+
+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.
+
+
+
- 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/configuration/stt_tts.mdx b/content/docs/configuration/stt_tts.mdx
index deca94586..a6ea4336d 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. 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)
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.
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/authentication.mdx b/content/docs/features/authentication.mdx
index 8235bd315..16a122f46 100644
--- a/content/docs/features/authentication.mdx
+++ b/content/docs/features/authentication.mdx
@@ -20,6 +20,27 @@ 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
+
+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**.
+
+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).
+
+
+
+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.
+
+
+
-## 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.
+
+
diff --git a/content/docs/local/index.mdx b/content/docs/local/index.mdx
index 0373e3297..2a4a35aac 100644
--- a/content/docs/local/index.mdx
+++ b/content/docs/local/index.mdx
@@ -5,3 +5,11 @@ description: How to install LibreChat locally
---
+
+
+
+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.
+
+
diff --git a/content/docs/quick_start/custom_endpoints.mdx b/content/docs/quick_start/custom_endpoints.mdx
index 8597153a2..ddfc39252 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.
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/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.
*
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',
+ ],
]
/**