diff --git a/docs-site/content/kagent/1.x/setup/installation.md b/docs-site/content/kagent/1.x/setup/installation.md index 032580f1..62cea1ec 100644 --- a/docs-site/content/kagent/1.x/setup/installation.md +++ b/docs-site/content/kagent/1.x/setup/installation.md @@ -291,6 +291,8 @@ The kagent chart connects the controller to Agent Substrate and creates a Worker kubectl port-forward -n kagent svc/{{< reuse "kagent-docs/snippets/name-ui.md" >}} 8082:8080 ``` + To serve the UI behind a reverse proxy under a prefix such as `/ui` (configurable with `ui.basePath`), see [Serve the UI under a sub-path]({{< link path="setup/reverse-proxy" >}}). + ## Next steps {{< cards >}} diff --git a/docs-site/content/kagent/1.x/setup/reverse-proxy.md b/docs-site/content/kagent/1.x/setup/reverse-proxy.md new file mode 100644 index 00000000..b8e42e83 --- /dev/null +++ b/docs-site/content/kagent/1.x/setup/reverse-proxy.md @@ -0,0 +1,135 @@ +--- +title: Serve the UI under a sub-path +description: Serve the kagent UI behind a reverse proxy using ui.basePath, including both proxy shapes, validation, and oauth2-proxy configuration. +weight: 15 +author: kagent.dev +--- + +The kagent UI is a static bundle served by nginx, which answers on port `8080` inside the pod. By default it serves at the root (`/`). When a reverse proxy fronts the UI and you want it reachable under a prefix such as `/ui`, set `ui.basePath` in the Helm values. + +## The `ui.basePath` value + +One Helm value controls the whole sub-path, and it is purely additive: leaving it unset leaves an existing installation serving at the root exactly as before. Set it, and the prefix reaches every URL the UI emits, not only the pages a reader navigates to. + +| Setting | Default | Effect | +|---------|---------|--------| +| `ui.basePath` | `""` (empty) | UI served at `/`. All existing behaviour unchanged. | +| `ui.basePath` | e.g. `/ui` | UI served under the prefix. One value moves every root-relative URL the UI emits. | + +When set, a single value controls the `` tag, the client-side router basename, and the root-relative API, SSO, userinfo, and share-link URLs that the browser uses. `publicBackendUrl` and the other root-relative URLs inherit the prefix, so do not adjust them by hand. + +## Setting the value + +Put `ui.basePath` in the Helm values at install time, rather than in a follow-up `helm upgrade`. The install steps live on the [Install kagent]({{< link path="setup/installation" >}}) page; the value is the only non-default setting this page needs. + +```bash +helm upgrade --install kagent \ + {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ + --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ + --namespace kagent --create-namespace --timeout 10m \ + --set ui.basePath=/ui +``` + +## Proxy shapes + +Both of the following proxy configurations work. Choose the one that matches your infrastructure. If you enable oauth2-proxy for SSO, the proxy must strip the prefix: with the prefix forwarded, oauth2-proxy cannot match its own sign-in and skip-authentication paths, and sign-in fails. + +### Proxy strips the prefix + +The proxy removes the `/ui` prefix before forwarding to the UI service on port `8080`. The UI then sees requests at `/` and serves them normally. + +Example nginx configuration: + +```nginx +location /ui/ { + proxy_pass http://{{< reuse "kagent-docs/snippets/name-ui.md" >}}.kagent:8080/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +} +``` + +A trailing slash on `proxy_pass` makes nginx drop the `/ui` prefix. + +### Proxy forwards the prefix unchanged + +The proxy passes `/ui/...` through to the UI service without modification. In this case nginx inside the pod strips the prefix itself using the rewrite rules rendered from `ui.basePath`. + +Example nginx configuration: + +```nginx +location /ui/ { + proxy_pass http://{{< reuse "kagent-docs/snippets/name-ui.md" >}}.kagent:8080; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +} +``` + +Do not put a trailing slash on `proxy_pass`. The full path including `/ui` reaches the UI pod, and its nginx rewrites it to `/` before serving. + +## Helm render validation + +Two validations run at Helm render time and fail the install rather than producing a broken UI. + +| Condition | Helm error message | +|-----------|-------------------| +| Not a path like `/ui`, or containing a `.` or `..` segment | `ui.basePath must be a path like /ui` | +| Starting with a path nginx serves | `ui.basePath cannot start with a path nginx serves, such as /api or /assets` | + +The reserved set that triggers the second rejection is larger than the message names. The full set of paths that must not appear as the first segment of `ui.basePath`: + +- `api` +- `a2a` +- `assets` +- `health` +- `env-config.js` +- `index.html` +- `mockServiceWorker.js` + +Choosing any of these as the base path collides with a path nginx already owns inside the UI pod. + +## oauth2-proxy + +If you use oauth2-proxy for SSO, a base-path change requires a second, manual step. + +1. Set `OIDC_REDIRECT_URL` under `oauth2-proxy.extraEnv` in your Helm values so that it points under the base path, for example `https://example.com/ui/oauth2/callback` rather than `https://example.com/oauth2/callback`. This becomes oauth2-proxy's `--redirect-url`, which is the OAuth callback, not the path that starts sign-in; the chart leaves its `--proxy-prefix` at `/oauth2`, so the callback is `/oauth2/callback`. +2. Register that same URL as a redirect URI with your identity provider. +3. Restart the oauth2-proxy pod after changing the value. + +The restart is required because the chart's sign-in HTML redirects to `{basePath}/login`, but the checksum that would roll the oauth2-proxy pod is computed where the `ui` values key is not in scope. The base path is absent from the hash, so the hash does not change and the pod keeps the old sign-in template and redirect URL until you restart it. + +## Verifying the installation + +After installing with `--set ui.basePath=/ui`, run each check and look for the named result. + +1. Open `/ui/` in a browser. Expected: the kagent UI loads, and the `` in the page source is `/ui/`. +2. Navigate to an agent list or detail page. Expected: the URL stays under `/ui/...`, for example `/ui/agents`. +3. Open the browser network tab while the page loads. Expected: API calls go to `/ui/api/...`, not `/api/...`. +4. Reload the page. Expected: the SPA router keeps the `/ui` prefix in the URL. +5. If oauth2-proxy is enabled, sign in. Expected: the OAuth redirect lands under `/ui/oauth2/...`, then returns you to the UI under `/ui/`. This holds when the proxy strips the prefix; when the proxy forwards the prefix unchanged, the OAuth paths behave as described under [Proxy shapes](#proxy-shapes). + +To confirm the Helm-side validation, install with a rejected value and expect the render to fail: + +```bash +helm upgrade --install kagent \ + {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ + --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ + --namespace kagent --create-namespace \ + --set ui.basePath=/api +``` + +Expected: + +``` +Error: execution error at (kagent/templates/ui-deployment.yaml:21:31): ui.basePath cannot start with a path nginx serves, such as /api or /assets +``` + +## Next steps + +{{< cards >}} + {{< card link=`{{< link path="setup/installation" >}}` title="Install kagent" subtitle="The full install steps this page's `ui.basePath` value plugs into." >}} + {{< card link=`{{< link path="substrate-runtime/identity" >}}` title="Identity" subtitle="How kagent resolves a caller's identity and scopes a Session to its creator." >}} +{{< /cards >}}