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

> ## Agent Instructions
> These docs cover three products: TrustGate (AI agent gateway), TrustGuard (runtime security), and TrustTest (AI red teaming). Start from each product overview for the definition and How it works. Prefer the .md URL next to a page in /llms.txt when you need the full article. Use /llms-full.txt for a single-file dump of the site.

# Open WebUI

> Put Open WebUI's chat behind TrustGate: one connection for every model, and every request attributed to the person who sent it — declared by Open WebUI, or verified by your identity provider.

[Open WebUI](https://openwebui.com) is a self-hosted chat front-end for LLMs. It talks
to any OpenAI-compatible API, so it connects to TrustGate's LLM plane like any other
OpenAI provider: one URL, one key, and every model the application routes to shows up
in its model picker.

A chat front-end serves many people through one connection. Open WebUI can tell
TrustGate who each request is for, so **Activity**, analytics and cost reports show the
person, not just the application. How much you can trust that name depends on the mode
you pick — see [Choose how users are identified](#choose-how-users-are-identified).

## Integration capabilities

| Product | What it does | What it controls |
| - | - | - |
| **[TrustGate](/trustgate/overview)** | Routes each chat request and applies the application's governance: allowed models, rate limits, budgets, and [TrustGuard](/trustguard/overview) policies | Which models · spend ceilings · content policy |
| **Open WebUI** | Serves the chat UI, signs users in, and forwards who each request is for | Who can use the chat |

## Before you start

| Requirement | Notes |
| - | - |
| A TrustGate application with an LLM plane | Create it in the console and issue its API key. The key is shown once. See [Applications](/trustgate/access/applications). |
| Its **LLM Gateway URL** and slug | Settings → General, or the application's Connect tab. See [Connect](/trustgate/llm/connect). |
| Admin access to Open WebUI | Connections and environment variables are admin settings. |

<Steps>
  <Step title="Add TrustGate as a connection" titleSize="h2" id="1-add-trustgate-as-a-connection">
    In Open WebUI, open **Admin Panel → Settings → Connections**, and under **OpenAI API** add
    a connection:

    | Field | Value |
    | - | - |
    | **URL** | `https://<llm-host>/<application-slug>/v1` |
    | **Auth** | **Bearer**, with the application's key (`ag_…`) |

    Or set it from the environment where Open WebUI runs:

    ```yaml theme={null}
    # docker-compose.yml — Open WebUI
    services:
      open-webui:
        environment:
          - OPENAI_API_BASE_URL=https://<llm-host>/<application-slug>/v1
          - OPENAI_API_KEY=<your TrustGate application key>
    ```

    If Open WebUI runs in Docker and TrustGate on the same host, use
    `host.docker.internal` instead of `localhost`.
  </Step>

  <Step title="Check the models" titleSize="h2" id="2-check-the-models">
    Open WebUI lists models from the application's [`/v1/models`](/trustgate/endpoints/models),
    so the picker shows what the application routes to. To show fewer, or to add a value the
    list does not carry — `auto` for an application that load-balances — use the connection's
    **Model IDs** filter. See [What goes in `model`](/trustgate/llm/connect#what-goes-in-model).

    Send a chat and open **Activity** in the console: the request is there, under the
    application.
  </Step>

  <Step title="Identify your users" titleSize="h2" id="3-identify-your-users">
    Without this, every request is the application's. Pick a mode below and turn it on;
    nothing changes on TrustGate's side for the first two.
  </Step>
</Steps>

## Choose how users are identified

| Mode | Open WebUI sends | Verified | What Activity shows |
| - | - | - | - |
| [User headers](#user-headers) | `X-OpenWebUI-User-*` headers | No | The user, marked unverified |
| [Signed user JWT](#signed-user-jwt) | One HS256 token in `X-OpenWebUI-User-Jwt` | No | The user, marked unverified |
| [SSO token](#verified-users-with-sso) | Each user's own IdP access token, instead of the key | **Yes** | The user, verified |

The first two are **declared**: TrustGate records who Open WebUI says it was serving, for
telemetry and cost attribution, and never lets the name authorize anything — see
[End-user attribution](/trustgate/observability/end-user-attribution). The third is
**authenticated**: TrustGate verifies the token, and the person becomes the request's
principal, which policies can act on.

### User headers

Set one variable where Open WebUI runs. It is off by default:

```yaml theme={null}
services:
  open-webui:
    environment:
      - ENABLE_FORWARD_USER_INFO_HEADERS=true
```

Open WebUI then adds these to every request, and TrustGate reads them with no setup:

| Header | Recorded as |
| - | - |
| `X-OpenWebUI-User-Id` | `id` — Open WebUI's own user id |
| `X-OpenWebUI-User-Email` | `email` |
| `X-OpenWebUI-User-Name` | `name` |
| `X-OpenWebUI-User-Role` | `role` |
| `X-OpenWebUI-Chat-Id` | `session_id` — one per conversation |

Open WebUI percent-encodes the name (`José` arrives as `Jos%C3%A9`); TrustGate decodes it.
If a proxy of your own sits between Open WebUI and TrustGate, check that it does not strip
unknown request headers.

### Signed user JWT

Add a secret, and Open WebUI **stops** sending the user headers and sends one token in
their place:

```yaml theme={null}
services:
  open-webui:
    environment:
      - ENABLE_FORWARD_USER_INFO_HEADERS=true
      - FORWARD_USER_INFO_HEADER_JWT_SECRET=<a secret you choose>
```

The token is HS256, issued by `open-webui`, and expires after five minutes by default. TrustGate reads
the user from its claims, again with no setup:

| Claim | Recorded as |
| - | - |
| `sub` | `id` |
| `email` | `email` |
| `name` | `name` |
| `role` | `role` |

<Note>
  TrustGate does **not** check the token's signature: that takes the secret you set in Open
  WebUI, which the gateway does not hold. The token is therefore worth what the user headers
  were — anyone holding the application key could mint one — and is recorded the same way:
  declared, never the principal. The token is signed, not encrypted: anyone who sees the
  request can read the user in it. For a user TrustGate verifies, use
  [SSO](#verified-users-with-sso).
</Note>

Keep the default header name. If you rename it with `FORWARD_USER_INFO_HEADER_JWT`,
TrustGate does not find it and the user is not recorded.

### Verified users with SSO

When Open WebUI signs users in with your identity provider, its connection to TrustGate can
send each person's own access token instead of the application key. TrustGate validates the
token — signature against the IdP's keys, issuer, expiry, audience — and records the person
it names as the request's principal.

You configure three places: your IdP, NeuralTrust, and Open WebUI. Have these at hand:

| Value | Where it comes from |
| - | - |
| **Issuer** | Your IdP. Entra: `https://login.microsoftonline.com/{tenant_id}/v2.0`. Okta: `https://{okta_domain}/oauth2/{auth_server_id}`. |
| **Audience** | The API you expose for TrustGate on the IdP, e.g. `api://trustgate`. |
| **Open WebUI's client ID** | The OAuth client Open WebUI signs in with. It is the token's `azp`. |

<Steps>
  <Step title="On your IdP: issue tokens for TrustGate">
    Expose an API for TrustGate with a delegated scope users can be granted, and let Open WebUI's
    client request it. The token Open WebUI receives must carry TrustGate's audience in `aud`.

    * **Entra ID:** on the TrustGate app registration, **Expose an API** with a scope such as
      `api://trustgate/access`, then add that scope to Open WebUI's app registration under
      **API permissions**. See [Entra ID](/trustgate/concepts/authorization/entra-id).
    * **Okta:** use an authorization server whose audience is TrustGate's, with a scope Open
      WebUI's client may request. See [Okta](/trustgate/concepts/authorization/okta).
    * **Keycloak:** add an **Audience** mapper to Open WebUI's client that includes TrustGate's
      audience in the access token.
  </Step>

  <Step title="In NeuralTrust: add the trust anchor">
    Go to **Settings → Agent Gateway → Machine identity** → **Add trust anchor**:

    * **Kind**: **IdP (JWT)**
    * **Name**: e.g. `company-idp`
    * **Issuer**: your IdP's issuer, exactly as it appears in the token's `iss`
    * **Audience**: TrustGate's audience

    Save. No secret is stored: the gateway only verifies tokens. TrustGate finds the signing
    keys from the issuer; pin a **JWKS URL** under **Advanced** if your IdP does not publish
    discovery.
  </Step>

  <Step title="In NeuralTrust: enter the application through the IdP">
    Go to **Agent Gateway → Applications**, open the application Open WebUI connects to, and on
    **General → Authentication** choose **Identity provider** and select the anchor:

    * **Allowed client IDs**: Open WebUI's client ID, so only tokens issued to Open WebUI get in.
      Leave it empty only when the audience is used by this application alone.

    **Save changes**. Providers and models stay as they were.

    <Warning>
      An application is entered one way: with its key **or** through an identity provider. Once it
      trusts the IdP, the application key no longer opens it. If other clients still call this
      application with the key, create a separate application for Open WebUI instead.
    </Warning>
  </Step>

  <Step title="In Open WebUI: sign in with the same IdP">
    Configure Open WebUI's OIDC login against that provider and ask for TrustGate's scope with
    the usual login scopes. Without it, many IdPs issue a token for a default resource, such as
    Microsoft Graph for Entra, and TrustGate rejects it:

    ```yaml theme={null}
    services:
      open-webui:
        environment:
          - OPENID_PROVIDER_URL=https://<your-idp>/.well-known/openid-configuration
          - OAUTH_CLIENT_ID=<Open WebUI's client id>
          - OAUTH_CLIENT_SECRET=<client secret>
          - OAUTH_SCOPES=openid email profile <TrustGate's scope>
          - ENABLE_OAUTH_SIGNUP=true
    ```

    Users who were already signed in must sign out and back in to get a token with the new
    scope. A user signed in with a local Open WebUI account has no IdP token to send.
  </Step>

  <Step title="In Open WebUI: switch the connection to OAuth">
    In **Admin Panel → Settings → Connections**, edit the TrustGate connection and set **Auth**
    to **OAuth**. The URL stays the same. Open WebUI now sends the signed-in user's token as
    `Authorization: Bearer` on each request, in place of the application key.

    Keep `ENABLE_FORWARD_USER_INFO_HEADERS` on if you like: when the user Open WebUI declares has
    the same email as the verified principal, Activity shows them once, as verified.
  </Step>

  <Step title="Check it in NeuralTrust">
    Send a chat, then open **Activity** in the console. The request shows the person as a
    **verified** user, and opening it shows how it authenticated.

    | Response | Meaning |
    | - | - |
    | `401` | The token failed verification: wrong issuer, wrong audience, or expired. |
    | `403` | The token verified, but its `azp` is not in **Allowed client IDs**. |
  </Step>
</Steps>

The principal is recorded as `principal_subject` (the token's `sub`) and `principal_email`,
the token's `email` claim or, when the IdP sends none, the first of `emailAddress`,
`preferred_username`, `upn` and `unique_name` that is an address.

## Troubleshooting

| Symptom | Cause |
| - | - |
| Activity shows the application but no user | `ENABLE_FORWARD_USER_INFO_HEADERS` is off, a proxy strips the headers, or `FORWARD_USER_INFO_HEADER_JWT` renamed the token header. |
| Every chat is `401` after switching to OAuth | The token's audience or issuer is not the trust anchor's: add TrustGate's scope to `OAUTH_SCOPES` and sign in again. Or the user signed in to Open WebUI with a local account, which has no IdP token to send. |
| Every chat is `403` after switching to OAuth | Open WebUI's client ID is missing from the application's **Allowed client IDs**. |
| Other clients stopped working | The application now trusts the IdP, so its key no longer opens it. Give Open WebUI its own application. |
| New SSO users see "Account activation pending" | Open WebUI's default role for new sign-ups is `pending`. Approve them in **Admin Panel → Users**, or set `DEFAULT_USER_ROLE=user`. |
| The user shows as unverified with SSO on | The declared email differs from the token's: Activity shows the declared user, unverified, so a mismatch is visible rather than hidden. |

## Related

* [End-user attribution](/trustgate/observability/end-user-attribution): every source TrustGate reads, and how declared users differ from principals
* [Authentication](/trustgate/concepts/auth): trusting an identity provider and admitting its tokens
* [Connect](/trustgate/llm/connect): the base URL, the key, and what goes in `model`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.