> ## Documentation Index
> Fetch the complete documentation index at: https://docs.r5d.chat/llms.txt
> Use this file to discover all available pages before exploring further.

# Model providers

> Connect OpenAI-compatible endpoints, configure models, and understand the outbound request guard.

Each user configures their own model providers under **Settings → Providers**.
Nothing is shared between users, and credentials never leave the control plane.

## Add a provider

A provider is any endpoint implementing the OpenAI API surface.

<Steps>
  <Step title="Enter the base URL">
    Use the URL that has `/chat/completions` and `/models` beneath it, usually
    ending in `/v1`.

    | Service    | Base URL                         |
    | ---------- | -------------------------------- |
    | OpenAI     | `https://api.openai.com/v1`      |
    | OpenRouter | `https://openrouter.ai/api/v1`   |
    | Groq       | `https://api.groq.com/openai/v1` |
    | Together   | `https://api.together.xyz/v1`    |
    | vLLM       | `https://your-host/v1`           |
  </Step>

  <Step title="Add the API key">
    The key is encrypted with `SETTINGS_ENCRYPTION_KEY` before storage. It is
    write-only: the API returns a `hasApiKey` flag and never the value itself.
  </Step>

  <Step title="Add custom headers if needed">
    Some gateways require extra headers, such as OpenRouter's `HTTP-Referer` and
    `X-Title`.

    <Warning>
      Custom headers are different from API keys. Because they must remain
      editable in the UI, `GET /api/providers` decrypts them for the
      authenticated owner. Treat header values as secrets visible to any client
      logged in as that user.
    </Warning>
  </Step>

  <Step title="Test the connection">
    **Test connection** requests `<base URL>/models` through the outbound guard.
    This validates the URL, the key, and network reachability in one step.
  </Step>
</Steps>

## Add models

A provider holds any number of models. Each needs:

<ResponseField name="Provider model ID" type="string" required>
  The exact identifier the provider expects, such as `gpt-4o-mini` or
  `anthropic/claude-sonnet-4`.
</ResponseField>

<ResponseField name="Display name" type="string" required>
  The label shown in the model picker.
</ResponseField>

<ResponseField name="Context window" type="integer" required>
  Total token limit for the model. Used to decide when to trim context.
</ResponseField>

<ResponseField name="Maximum output tokens" type="integer" required>
  Upper bound on a single response. Setting this above the provider's real limit
  causes request errors.
</ResponseField>

## Default model rules

Exactly one model per user is the default, enforced by the
`models_one_default_per_user` partial unique index.

* **Make default** releases the current holder before setting the new one,
  because the index rejects the opposite order.
* Deleting or disabling the default model, or the provider that owns it,
  automatically promotes the oldest remaining enabled model.
* A user therefore always has a usable default as long as one enabled model
  exists.

## The outbound request guard

Every outbound call is checked before it is made. By default the guard rejects:

* Non-HTTPS URLs
* URLs containing embedded credentials
* Private, loopback, and link-local addresses
* Cloud metadata endpoints
* Redirects to any of the above

<Tabs>
  <Tab title="Public providers">
    No configuration needed. Public HTTPS endpoints pass the guard.
  </Tab>

  <Tab title="In-cluster or LAN endpoints">
    A model server inside your network needs private egress:

    ```yaml theme={null}
    ALLOW_PRIVATE_EGRESS: 'true'
    ```

    This permits SSRF against internal services, so pair it with NetworkPolicy
    restricting where the control plane may connect.
  </Tab>

  <Tab title="Plain HTTP endpoints">
    Only for trusted private networks, since credentials travel unencrypted:

    ```yaml theme={null}
    ALLOW_INSECURE_HTTP: 'true'
    ```
  </Tab>
</Tabs>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Test connection fails with a blocked destination">
    The guard rejected the URL. Confirm it is HTTPS and publicly resolvable, or
    enable `ALLOW_PRIVATE_EGRESS` for an internal endpoint.
  </Accordion>

  <Accordion title="401 or 403 from the provider">
    The stored key is wrong or lacks access to the requested model. Keys are
    write-only, so re-enter it rather than trying to read it back.
  </Accordion>

  <Accordion title="404 on a model that exists">
    The base URL is probably missing or duplicating its version segment. The
    application appends `/chat/completions`, so the base URL should end at `/v1`.
  </Accordion>

  <Accordion title="Requests fail once conversations get long">
    The configured context window exceeds the model's real limit. Lower it to the
    provider's documented value.
  </Accordion>

  <Accordion title="Everything broke after a key rotation">
    If `SETTINGS_ENCRYPTION_KEY` changed, stored credentials cannot be
    decrypted. Restore the original key or have users re-enter their
    credentials.
  </Accordion>
</AccordionGroup>

## Backup implications

Provider credentials are encrypted in the database with the installation key.
The database and `SETTINGS_ENCRYPTION_KEY` form a single recovery unit; a
database restore without the matching key yields unreadable credentials.

<Card title="Backup and restore" icon="database" href="/operate/backup-restore">
  What to back up and how to validate a restore.
</Card>
