diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index 1fd00c082..b9dc5c61d 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -14,7 +14,15 @@ assignees: '' - [ ] Confirm that the upgrade and release note pages are updated. Typically, this is a separate issue. - [ ] In `content/docs`, create the new `main` by making a copy of the current main directory. Rename the directories appropriately. For example, the old main is copied to a new `main`, the old main becomes `latest`, and the old latest version gets deleted (agentgateway only maintains `n` latest and `n+1` main in development). +- [ ] For the _index.md pages of each version, update the version in the title. - [ ] In the `latest` version of the `content/docs` you just copied, update the test paths from `main` to `latest`. + Example search: + ``` + In ./website/content/docs/standalone/latest + Find file: content/docs/standalone/main/ + Replace with file: content/docs/standalone/latest/ + # and repeat for kubernetes + ``` - [ ] In the `hugo.yaml` file, update the `versions` to include the new minor version as main, the previous main as latest, and the previous latest as its version. - [ ] Update the version conrefs in the `assets/docs/versions` directory. Often, there is not a release for the next version, so you might have to use the same for latest and main. - [ ] Add the release date to the version table in `assets/docs/pages/reference/versions.md`, and remove any retired versions. diff --git a/assets/agw-docs/pages/cli/_index.md b/assets/agw-docs/pages/cli/_index.md deleted file mode 100644 index 3e02e0151..000000000 --- a/assets/agw-docs/pages/cli/_index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: CLI -weight: 10 -description: ---- - -{{< cards >}} -{{< card link="install" title="Install the kgwctl CLI">}} -{{< card link="visualize" title="Visualize resource dependencies">}} -{{< card link="commands" title="kgwctl commands" >}} -{{< /cards >}} diff --git a/assets/agw-docs/pages/cli/commands/_index.md b/assets/agw-docs/pages/cli/commands/_index.md deleted file mode 100644 index af6d556fa..000000000 --- a/assets/agw-docs/pages/cli/commands/_index.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Commands -weight: 30 -description: ---- - - -{{< cards >}} -{{< card link="kgwctl-analyze" title="kgwctl analyze" >}} -{{< card link="kgwctl-check" title="kgwctl check" >}} -{{< card link="kgwctl-completion" title="kgwctl completion" >}} -{{< card link="kgwctl-describe" title="kgwctl describe" >}} -{{< card link="kgwctl-get" title="kgwctl get" >}} -{{< card link="kgwctl-help" title="kgwctl help" >}} -{{< /cards >}} diff --git a/assets/agw-docs/pages/cli/commands/kgwctl-analyze.md b/assets/agw-docs/pages/cli/commands/kgwctl-analyze.md deleted file mode 100644 index 920b9dc35..000000000 --- a/assets/agw-docs/pages/cli/commands/kgwctl-analyze.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: kgwctl analyze -weight: 20 -description: kgwctl analyze command reference ---- - -Analyze kgateway resource manifests by file names or directory to find configuration issues and impacts on other resources. - -## Usage - -```sh -kgwctl analyze -f FILENAME|DIRECTORY [flags] -``` - -## Command-specific flags - -```yaml --f, --filename strings The file name or directory that contains the kgateway manifests that you want to analyze. --h, --help Help for the command. --R, --recursive Process the directory that you reference in -f or --filename recursively. This option is useful for when you want to analyze related manifests that are stored in the same directory. (default true) -``` - -{{< reuse "agw-docs/snippets/kgwctl-global-flags.md" >}} diff --git a/assets/agw-docs/pages/cli/commands/kgwctl-check.md b/assets/agw-docs/pages/cli/commands/kgwctl-check.md deleted file mode 100644 index 34d5864ca..000000000 --- a/assets/agw-docs/pages/cli/commands/kgwctl-check.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: kgwctl check -weight: 20 -description: kgwctl check command reference ---- - -Check the health of the kgateway control and data plane. - -## Usage - -```sh -kgwctl check [flags] -``` - -## Command-specific flags - -```yaml --h, --help Help for the command. -``` - -{{< reuse "agw-docs/snippets/kgwctl-global-flags.md" >}} - - - diff --git a/assets/agw-docs/pages/cli/commands/kgwctl-completion.md b/assets/agw-docs/pages/cli/commands/kgwctl-completion.md deleted file mode 100644 index 7115f26e0..000000000 --- a/assets/agw-docs/pages/cli/commands/kgwctl-completion.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: kgwctl completion -weight: 20 -description: kgwctl completion command reference ---- - -Generate the autocompletion script for the `kgwctl` CLI for the specified shell. For details on how to use the generated script, see the subcommand's help, such as `kgwctl completion bash --help`. - -## Usage - -```sh -kgwctl completion [command] -``` - -## Available commands - -```sh -bash Generate the autocompletion script for bash. -fish Generate the autocompletion script for fish. -powershell Generate the autocompletion script for powershell. -zsh Generate the autocompletion script for zsh. -``` - -## Command-specific flags - -```sh --h, --help Help for the command. -``` - -{{< reuse "agw-docs/snippets/kgwctl-global-flags.md" >}} - diff --git a/assets/agw-docs/pages/cli/commands/kgwctl-describe.md b/assets/agw-docs/pages/cli/commands/kgwctl-describe.md deleted file mode 100644 index d80ffcadc..000000000 --- a/assets/agw-docs/pages/cli/commands/kgwctl-describe.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: kgwctl describe -weight: 20 -description: kgwctl describe command reference ---- - -Get the details of one or more kgateway resources. - -## Usage - -```sh -kgwctl describe TYPE [RESOURCE_NAME] [flags] -``` - -## Command-specific flags - -```yaml --A, --all-namespaces If present, list the requested objects across all namespaces. Note that any namespace context that you specified with -n or --namespace is ignored. --h, --help Help for the command. --l, --selector string The selector label to filter on. Supported operations include '=', '==', and '!='. For example, to describe all resources with the `key1=value1` and `key2=value2` labels, use `-l key1=value1,key2=value2`. -``` - - -{{< reuse "agw-docs/snippets/kgwctl-global-flags.md" >}} \ No newline at end of file diff --git a/assets/agw-docs/pages/cli/commands/kgwctl-get.md b/assets/agw-docs/pages/cli/commands/kgwctl-get.md deleted file mode 100644 index e87bf1de1..000000000 --- a/assets/agw-docs/pages/cli/commands/kgwctl-get.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: kgwctl get -weight: 20 -description: kgwctl get command reference ---- - -List one or more kgateway resources. - -## Usage - -```sh -kgwctl get TYPE [RESOURCE_NAME] [flags] -``` - -## Command-specific flags - -```sh --A, --all-namespaces If present, list the requested objects across all namespaces. Note that any namespace context that you specified with -n or --namespace is ignored. ---for string Only show the resources that match the filter. To filter the results, use the `TYPE[/NAMESPACE]/NAME` format. If no `NAMESPACE` is set, the `default` namespace is automatically assumed. Examples: `gateway/ns2/foo-gateway`, `httproute/bar-httproute`, and `service/ns1/my-svc`. --h, --help Help for the command. --l, --selector string The selector label to filter on. Supported operations include '=', '==', and '!='. For example, to get all resources with the `key1=value1` and `key2=value2` labels, use `-l key1=value1,key2=value2`. --o, --output string Define the output format. Supported values are wide, json, yaml, and graph. -``` - -{{< reuse "agw-docs/snippets/kgwctl-global-flags.md" >}} - diff --git a/assets/agw-docs/pages/cli/commands/kgwctl-help.md b/assets/agw-docs/pages/cli/commands/kgwctl-help.md deleted file mode 100644 index 762edd8a0..000000000 --- a/assets/agw-docs/pages/cli/commands/kgwctl-help.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: kgwctl help -weight: 20 -description: kgwctl help command reference ---- - -List all supported commands for the `kgwctl` CLI. - -## Usage - -```sh -kgwctl [command] -``` - -## Available Commands - -``` -analyze Analyze kgateway resource manifests by file names or directory to find configuration issues and impacts on other resources. -check Check the health of the kgateway control and data plane. -completion Generate the autocompletion script for the `kgwctl` CLI for the specified shell. -describe Get the details of one or more kgateway resources. -get List one or more kgateway resources. -help List all supported commands for the `kgwctl` CLI. -``` - -{{< reuse "agw-docs/snippets/kgwctl-global-flags.md" >}} \ No newline at end of file diff --git a/assets/agw-docs/pages/cli/install.md b/assets/agw-docs/pages/cli/install.md deleted file mode 100644 index 6a8528e70..000000000 --- a/assets/agw-docs/pages/cli/install.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Install the kgwctl CLI -weight: 10 -description: ---- - - - -Use the `kgwctl` CLI to quickly assess the health of your kgateway control and data plane components. - -1. Save the version of the kgwctl CLI that you want to install as an environment variable. - ```sh - export VERSION=v{{< reuse "agw-docs/versions/kgwctl.md" >}} - ``` - -2. Create a personal access token in GitHub. For more information, see the [GitHub documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). - -3. Install the `kgwctl` CLI. - ```sh - curl -sL -H "Authorization: token ${GITHUB_TOKEN}" https://raw.githubusercontent.com/solo-io/kgwctl-temp/refs/heads/main/scripts/install.sh | sh - - ``` - - Example output: - ```sh - Downloading kgwctl-darwin-arm64 from release v{{< reuse "agw-docs/versions/kgwctl.md" >}}... - Verifying checksum... - Checksum verified successfully - Download successful: kgwctl-darwin-arm64 - kgwctl CLI was successfully installed πŸŽ‰ - - Add the kgwctl CLI to your path with: - export PATH=$HOME/.kgwctl/bin:$PATH - ``` - -4. Add the CLI to your path. - ```sh - export PATH=$HOME/.kgwctl/bin:$PATH - ``` - -5. Try out the CLI. For example, you can list all supported commands with the following command. - ```sh - kgwctl --help - ``` - - Example output: - ```console - Commands for interacting with kgateway installations. - - Usage: - kgwctl [command] - - Available Commands: - analyze Analyze kgateway resource manifests by file names or directory to find configuration issues and impacts on other resources. - check Check the health of the kgateway control and data plane. - completion Generate the autocompletion script for the `kgwctl` CLI for the specified shell. - describe Get the details of one or more kgateway resources. - get List one or more kgateway resources. - help List all supported commands for the `kgwctl` CLI. - ... - ``` - \ No newline at end of file diff --git a/assets/agw-docs/pages/cli/visualize.md b/assets/agw-docs/pages/cli/visualize.md deleted file mode 100644 index 3602f10e6..000000000 --- a/assets/agw-docs/pages/cli/visualize.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Visualize resource dependencies -weight: 15 -description: Visualize the relationship and dependencies for all your kgateway and Kubernetes Gateway API resources in a DOT Graph. ---- - -Visualize the relationship and dependencies for all your kgateway and Kubernetes Gateway API resources in a DOT Graph. - -## About DOT Graphs - -A DOT graph is a plain text graph description language that is primarily used with [Graphviz](https://graphviz.org/). Graphviz is an open source graph visualization software to represent structural information and networks as diagrams, such as flowcharts, dependency trees, and state machines. - -The kgwctl CLI has built-in support for generating DOT graphs to show the dependencies and relationships of kgateway and Kubernetes Gateway API resources. - -## Before you begin - -{{< reuse "agw-docs/snippets/prereq.md" >}} - -## Visualize your resources - -1. Generate a graph for a particular resource in your cluster. The following command generates a graph for the http Gateway that you created in [before you begin](#before-you-begin) - - ```sh - kgwctl get gateway http -n kgateway-system -o graph > output.graph - ``` - -2. Open the [Graphviz online playground]( https://dreampuf.github.io/GraphvizOnline). - -3. Copy and paste the output of `output.graph` into the Graphviz online playground. The tool automatically generates the DOT graph for you. - - {{< reuse-image src="img/graphviz.svg" >}} \ No newline at end of file diff --git a/assets/agw-docs/pages/observability/control-plane-metrics.md b/assets/agw-docs/pages/observability/control-plane-metrics.md index 3f6241b4a..d4f59afa5 100644 --- a/assets/agw-docs/pages/observability/control-plane-metrics.md +++ b/assets/agw-docs/pages/observability/control-plane-metrics.md @@ -65,9 +65,9 @@ Helpful terms: * Transform: The process of the control plane converting high-level resources or intermediate representations (IR) into lower-level representations into the structure that the XDS API expects for a snapshot. -{{< version include-if="1.1.x" >}} +{{< version include-if="1.2.x" >}} {{< reuse "agw-docs/snippets/metrics-control-plane-latest.md" >}} {{< /version >}} -{{< version include-if="1.2.x" >}} +{{< version include-if="1.3.x" >}} {{< reuse "agw-docs/snippets/metrics-control-plane-main.md" >}} {{< /version >}} diff --git a/assets/agw-docs/pages/reference/versions.md b/assets/agw-docs/pages/reference/versions.md index f3a199715..b819fbcb8 100644 --- a/assets/agw-docs/pages/reference/versions.md +++ b/assets/agw-docs/pages/reference/versions.md @@ -4,6 +4,7 @@ Review the following information about supported release versions for the [agent | agentgateway | Release date | Kubernetes | Gateway API`*` | MCP spec | Helm | Istio`†` | |--------------|--------------|------------|----------------|----------|------|----------| +| 1.2.x | 14 May 2026 | 1.31 - 1.36 | 1.3 - 1.5`‑` | 2025-11-25 | >= 3.12 | 1.23 - 1.29 | | 1.1.x | 09 Apr 2026 | 1.31 - 1.35 | 1.3 - 1.5`‑` | 2025-06-18 | >= 3.12 | 1.23 - 1.29 | | 1.0.x | 16 Mar 2026 | 1.31 - 1.35 | 1.3 - 1.5`‑` | 2025-06-18 | >= 3.12 | 1.23 - 1.29 | | 2.2.x | 09 Feb 2026 | 1.31 - 1.35 | 1.2 - 1.4 | 2025-06-18 | >= 3.12 | 1.23 - 1.27 | diff --git a/assets/agw-docs/pages/security/external-auth.md b/assets/agw-docs/pages/security/external-auth.md index 631ff8e41..010fe7bb3 100644 --- a/assets/agw-docs/pages/security/external-auth.md +++ b/assets/agw-docs/pages/security/external-auth.md @@ -304,11 +304,9 @@ You can apply a policy at two levels: the Gateway level or the HTTPRoute level. ... ``` -{{< version include-if="1.2.x" >}} ## Conditional execution To choose between multiple external authorization servers based on the request, use the `conditional` field on your `extAuth` policy. For example, you can send admin paths to a stricter authorization server and route every other request to a standard one. For details, see [Conditional policies]({{< link-hextra path="/about/policies/conditional-policies" >}}). -{{< /version >}} ## Cleanup diff --git a/assets/agw-docs/pages/security/rate-limit-global.md b/assets/agw-docs/pages/security/rate-limit-global.md index 686e31437..a9dbe4b54 100644 --- a/assets/agw-docs/pages/security/rate-limit-global.md +++ b/assets/agw-docs/pages/security/rate-limit-global.md @@ -875,11 +875,9 @@ Apply both local and global rate limits to the same traffic. The first 50 requests succeed (global limit), then subsequent requests return 429. -{{< version include-if="1.2.x" >}} ## Conditional execution To apply different rate limits based on the request, use the `conditional` field on your `rateLimit` policy. For example, you can apply a strict per-IP limit to anonymous traffic and a higher per-user limit to authenticated traffic. For details, see [Conditional policies]({{< link-hextra path="/about/policies/conditional-policies" >}}). -{{< /version >}} ## Cleanup diff --git a/assets/agw-docs/pages/security/rate-limit-http.md b/assets/agw-docs/pages/security/rate-limit-http.md index dbbc528c5..02f46dc96 100644 --- a/assets/agw-docs/pages/security/rate-limit-http.md +++ b/assets/agw-docs/pages/security/rate-limit-http.md @@ -260,11 +260,9 @@ Local rate limiting runs independently on each proxy replica. If you run multipl For detailed instructions on setting up global rate limiting with descriptors and an external rate limit service, see the [Global rate limiting guide]({{< link-hextra path="/security/rate-limit-global" >}}). -{{< version include-if="1.2.x" >}} ## Conditional execution To apply different rate limits based on the request, use the `conditional` field on your `rateLimit` policy. For example, you can apply stricter limits on writes than on reads. For details, see [Conditional policies]({{< link-hextra path="/about/policies/conditional-policies" >}}). -{{< /version >}} ## Cleanup diff --git a/assets/agw-docs/pages/traffic-management/direct-response.md b/assets/agw-docs/pages/traffic-management/direct-response.md index 9e3677b83..0ec4c5b77 100644 --- a/assets/agw-docs/pages/traffic-management/direct-response.md +++ b/assets/agw-docs/pages/traffic-management/direct-response.md @@ -126,11 +126,9 @@ EOF Status: Healthy% ``` -{{< version include-if="1.2.x" >}} ## Conditional execution To return a direct response only when a CEL expression matches, use the `conditional` field on your `directResponse` policy. For example, you can return `410 Gone` on deprecated paths and let every other request reach the backend. For details, see [Conditional policies]({{< link-hextra path="/about/policies/conditional-policies" >}}). -{{< /version >}} ## Cleanup diff --git a/assets/agw-docs/versions/kgwctl.md b/assets/agw-docs/versions/kgwctl.md deleted file mode 100644 index db9376fb7..000000000 --- a/assets/agw-docs/versions/kgwctl.md +++ /dev/null @@ -1 +0,0 @@ -0.0.1-alpha4 \ No newline at end of file diff --git a/assets/agw-docs/versions/n-patch.md b/assets/agw-docs/versions/n-patch.md index ac89ebad4..bd6dec331 100644 --- a/assets/agw-docs/versions/n-patch.md +++ b/assets/agw-docs/versions/n-patch.md @@ -1 +1 @@ -{{< version include-if="1.1.x" >}}1.1.0{{< /version >}}{{< version include-if="1.2.x" >}}1.1.0{{< /version >}} \ No newline at end of file +{{< version include-if="1.1.x" >}}1.1.0{{< /version >}}{{< version include-if="1.2.x" >}}1.2.0{{< /version >}}{{< version include-if="1.3.x" >}}1.2.0{{< /version >}} \ No newline at end of file diff --git a/assets/agw-docs/versions/patch_n+1.md b/assets/agw-docs/versions/patch_n+1.md index ac89ebad4..280028bc4 100644 --- a/assets/agw-docs/versions/patch_n+1.md +++ b/assets/agw-docs/versions/patch_n+1.md @@ -1 +1 @@ -{{< version include-if="1.1.x" >}}1.1.0{{< /version >}}{{< version include-if="1.2.x" >}}1.1.0{{< /version >}} \ No newline at end of file +{{< version include-if="1.1.x" >}}1.2.0{{< /version >}}{{< version include-if="1.2.x" >}}1.2.0{{< /version >}}{{< version include-if="1.3.x" >}}1.2.0{{< /version >}} \ No newline at end of file diff --git a/assets/agw-docs/versions/patch_n-1.md b/assets/agw-docs/versions/patch_n-1.md index 7f207341d..2421e6384 100644 --- a/assets/agw-docs/versions/patch_n-1.md +++ b/assets/agw-docs/versions/patch_n-1.md @@ -1 +1 @@ -1.0.1 \ No newline at end of file +{{< version include-if="1.1.x" >}}1.0.1{{< /version >}}{{< version include-if="1.2.x" >}}1.1.0{{< /version >}}{{< version include-if="1.3.x" >}}1.2.0{{< /version >}} \ No newline at end of file diff --git a/assets/agw-docs/versions/short.md b/assets/agw-docs/versions/short.md index b123147e2..33697772f 100644 --- a/assets/agw-docs/versions/short.md +++ b/assets/agw-docs/versions/short.md @@ -1 +1 @@ -1.1 \ No newline at end of file +{{< version include-if="1.1.x" >}}1.1{{< /version >}}{{< version include-if="1.2.x" >}}1.2{{< /version >}}{{< version include-if="1.3.x" >}}1.3{{< /version >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/_index.md b/content/docs/kubernetes/1.1.x/_index.md new file mode 100644 index 000000000..b7fea4acd --- /dev/null +++ b/content/docs/kubernetes/1.1.x/_index.md @@ -0,0 +1,12 @@ +--- +title: Latest +weight: 460 +description: Use agentgateway in a Kubernetes environment. +test: skip +--- + +Welcome to the documentation for using agentgateway on Kubernetes! + +
+ +{{< reuse "agw-docs/pages/landing.md" >}} diff --git a/content/docs/kubernetes/1.1.x/about/_index.md b/content/docs/kubernetes/1.1.x/about/_index.md new file mode 100644 index 000000000..30c2b161a --- /dev/null +++ b/content/docs/kubernetes/1.1.x/about/_index.md @@ -0,0 +1,9 @@ +--- +title: About +weight: 10 +icon: info +test: skip +--- + +{{< reuse "agw-docs/snippets/agentgateway/about.md" >}} To learn more about {{< reuse "agw-docs/snippets/agentgateway.md" >}}, review the following topics. + diff --git a/content/docs/kubernetes/1.1.x/about/architecture.md b/content/docs/kubernetes/1.1.x/about/architecture.md new file mode 100644 index 000000000..d5b61206e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/about/architecture.md @@ -0,0 +1,7 @@ +--- +title: Architecture +weight: 15 +test: skip +--- + +{{< reuse "agw-docs/pages/about/architecture.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/about/gateway-api.md b/content/docs/kubernetes/1.1.x/about/gateway-api.md new file mode 100644 index 000000000..47cad0e26 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/about/gateway-api.md @@ -0,0 +1,7 @@ +--- +title: Gateway API +weight: 20 +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/gateway-api.md" >}} diff --git a/content/docs/kubernetes/1.1.x/about/overview.md b/content/docs/kubernetes/1.1.x/about/overview.md new file mode 100644 index 000000000..552e91771 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/about/overview.md @@ -0,0 +1,7 @@ +--- +title: Overview +weight: 10 +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/about.md" >}} diff --git a/content/docs/kubernetes/latest/about/policies.md b/content/docs/kubernetes/1.1.x/about/policies.md similarity index 100% rename from content/docs/kubernetes/latest/about/policies.md rename to content/docs/kubernetes/1.1.x/about/policies.md diff --git a/content/docs/kubernetes/1.1.x/agent/_index.md b/content/docs/kubernetes/1.1.x/agent/_index.md new file mode 100644 index 000000000..5d84a48ae --- /dev/null +++ b/content/docs/kubernetes/1.1.x/agent/_index.md @@ -0,0 +1,10 @@ +--- +title: Agent connectivity +weight: 60 +icon: smart_toy +description: +test: skip +--- + +Connect agentgateway proxies to AI agents. + diff --git a/content/docs/kubernetes/1.1.x/agent/a2a.md b/content/docs/kubernetes/1.1.x/agent/a2a.md new file mode 100644 index 000000000..9203167c3 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/agent/a2a.md @@ -0,0 +1,12 @@ +--- +title: Connect to an agent +weight: 40 +test: + a2a: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/agent/a2a.md + path: a2a +--- + +{{< reuse "agw-docs/pages/agentgateway/agent/a2a.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/agent/about.md b/content/docs/kubernetes/1.1.x/agent/about.md new file mode 100644 index 000000000..872acfd7b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/agent/about.md @@ -0,0 +1,8 @@ +--- +title: About +weight: 10 +description: +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/agent/about.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/inference.md b/content/docs/kubernetes/1.1.x/inference.md new file mode 100644 index 000000000..6a73cf534 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/inference.md @@ -0,0 +1,9 @@ +--- +title: Inference routing +weight: 30 +icon: alt_route +description: +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/inference.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/install/_index.md b/content/docs/kubernetes/1.1.x/install/_index.md new file mode 100644 index 000000000..7ba4dbf7a --- /dev/null +++ b/content/docs/kubernetes/1.1.x/install/_index.md @@ -0,0 +1,8 @@ +--- +title: Install +weight: 14 +icon: download +test: skip +--- + +Learn how to install the {{< reuse "/agw-docs/snippets/kgateway.md" >}} {{< gloss "Control Plane" >}}control plane{{< /gloss >}} to use with {{< reuse "/agw-docs/snippets/agentgateway.md" >}} {{< gloss "Data Plane" >}}data plane{{< /gloss >}} proxies. diff --git a/content/docs/kubernetes/1.1.x/install/advanced.md b/content/docs/kubernetes/1.1.x/install/advanced.md new file mode 100644 index 000000000..cf11521a7 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/install/advanced.md @@ -0,0 +1,121 @@ +--- +title: Advanced settings +weight: 70 +description: Install agentgateway and related components. +test: skip +--- + +{{< reuse "agw-docs/pages/install/advanced.md" >}} + +## Autoscaling + +You can configure Horizontal Pod Autoscaler or Vertical Pod Autoscaler policies for the {{< reuse "agw-docs/snippets/kgateway.md" >}} control plane. To set up these policies, you use the `horizontalPodAutoscaler` or `verticalPodAutoscaler` fields in the Helm chart. + +{{< callout type="info" >}} +Note that {{< reuse "agw-docs/snippets/kgateway.md" >}} uses leader election if multiple replicas are present. The elected leader's workload is typically larger than the workload of non-leader replicas and therefore drives the overall infrastructure cost. Because of that, Vertical Pod Autoscaling can be a reasonable solution to ensure that the elected leader has the resources it needs to perform its work successfully. In cases where the leader has a large workload, Horizontal Pod Autoscaling might not be as effective as it adds more replicas that do not reduce the workload of the elected leader. +{{< /callout >}} + +{{< callout type="warning" >}} +If you plan to set up both VPA and HPA policies, make sure to closely monitor performance and cost during scale up events. Using both policies can lead to conflict or even destructive loops that impact the performance of your control plane. +{{< /callout >}} + + +### Vertical Pod Autoscaler (VPA) + +Vertical Pod Autoscaler (VPA) is a Kubernetes component that automatically adjusts the CPU and memory reservations of your pods to match their actual usage. + +The following Helm configuration ensures that the control plane pod is always assigned a minimum of 0.1 CPU cores (100millicores) and 128Mi of memory. + +```yaml + +verticalPodAutoscaler: + updatePolicy: + updateMode: Auto + resourcePolicy: + containerPolicies: + - containerName: "*" + minAllowed: + cpu: 100m + memory: 128Mi +``` + +### Horizontal Pod Autoscaler (HPA) + +Horizontal Pod Autoscaler (HPA) adds more instances of the pod to your environment when certain memory or CPU thresholds are reached. + +In the following example, you want to have 1 control plane replica running at any given time. If the CPU utilization averages 80%, you want to gradually scale up your replicas. You can have a maximum of 5 replicas at any given time. +```yaml + +horizontalPodAutoscaler: + minReplicas: 1 + maxReplicas: 5 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 +``` + + +**Note**: To monitor the memory and CPU threshold, you need to deploy the Kubernetes `metrics-server` in your cluster. The `metrics-server` retrieves metrics, such as CPU and memory consumption for your workloads. + +You can install the server with the following command: +```sh +kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml +kubectl -n kube-system patch deployment metrics-server \ + --type=json \ + -p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]' +``` + +Then, start monitoring CPU and memory consumption with the `kubectl top pod` command. + +## PriorityClass + +You can assign a PriorityClassName to the control plane pods by using the Helm chart. [Priority](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) indicates the importance of a pod relative to other pods. If a pod cannot be scheduled, the scheduler tries to preempt (evict) lower priority pods to make scheduling of the pending pod possible. + +To assign a PriorityClassName to the control plane, you must first create a PriorityClass resource. The following example creates a PriorityClass with the name `system-cluster-critical` that assigns a priority of 1 Million. + +```yaml +kubectl apply -f- <}} Helm charts, including the Deployment, Service, ServiceAccount, and ClusterRoles. This allows you to better organize your resources or integrate with external tooling. + +The following snippet adds the `label-key` and `agw-managed` labels to all resources. + +```yaml + +commonLabels: + label-key: label-value + agw-managed: "true" +``` + + + + + + + + + diff --git a/content/docs/kubernetes/1.1.x/install/argocd.md b/content/docs/kubernetes/1.1.x/install/argocd.md new file mode 100644 index 000000000..5296bb135 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/install/argocd.md @@ -0,0 +1,12 @@ +--- +title: ArgoCD +weight: 10 +description: Install agentgateway and related components. +test: skip +--- + +{{< reuse "agw-docs/pages/install/argocd.md" >}} + + + + diff --git a/content/docs/kubernetes/1.1.x/install/flux.md b/content/docs/kubernetes/1.1.x/install/flux.md new file mode 100644 index 000000000..aaf8d3152 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/install/flux.md @@ -0,0 +1,8 @@ +--- +title: FluxCD +weight: 11 +description: Install agentgateway and related components. +test: skip +--- + +{{< reuse "agw-docs/pages/install/flux.md" >}} diff --git a/content/docs/kubernetes/1.1.x/install/helm.md b/content/docs/kubernetes/1.1.x/install/helm.md new file mode 100644 index 000000000..6ca05451b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/install/helm.md @@ -0,0 +1,18 @@ +--- +title: Helm +weight: 5 +description: Install agentgateway and related components. +test: + helm-install: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + helm-install-experimental: + - file: content/docs/kubernetes/latest/install/helm.md + path: experimental +--- + +{{< reuse "agw-docs/pages/install/helm.md" >}} + + + + diff --git a/content/docs/kubernetes/1.1.x/install/sample-app.md b/content/docs/kubernetes/1.1.x/install/sample-app.md new file mode 100644 index 000000000..9c8f8968b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/install/sample-app.md @@ -0,0 +1,15 @@ +--- +title: Sample app +weight: 20 +description: Set up the httpbin sample app to try traffic management, security, and resiliency guides. +test: + install-httpbin: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin +--- + +{{< reuse "agw-docs/pages/install/sample-app.md" >}} diff --git a/content/docs/kubernetes/1.1.x/install/tls.md b/content/docs/kubernetes/1.1.x/install/tls.md new file mode 100644 index 000000000..ecadb654e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/install/tls.md @@ -0,0 +1,11 @@ +--- +title: TLS encryption +weight: 100 +test: skip +--- + +{{< reuse "agw-docs/pages/install/tls.md" >}} + + + + diff --git a/content/docs/kubernetes/1.1.x/integrations/_index.md b/content/docs/kubernetes/1.1.x/integrations/_index.md new file mode 100644 index 000000000..24e3cd3af --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/_index.md @@ -0,0 +1,10 @@ +--- +title: Integrations +weight: 650 +icon: hub +description: Connect agentgateway with third-party tools and platforms. +test: skip +--- + +Extend agentgateway capabilities by integrating with external tools and platforms for observability, authentication, and more. + diff --git a/content/docs/kubernetes/1.1.x/integrations/argo.md b/content/docs/kubernetes/1.1.x/integrations/argo.md new file mode 100644 index 000000000..9d6f24162 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/argo.md @@ -0,0 +1,7 @@ +--- +title: Argo Rollouts +weight: 10 +description: Use agentgateway with Argo Rollouts. +--- + +{{< reuse "agw-docs/pages/integrations/argo.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/_index.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/_index.md new file mode 100644 index 000000000..6e77c28b1 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/_index.md @@ -0,0 +1,15 @@ +--- +title: LLM client configuration +weight: 10 +description: Configure AI coding tools and applications to use agentgateway running in Kubernetes +test: + llm-clients-k8s-gateway-url: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/integrations/llm-clients/_index.md + path: llm-clients-k8s-gateway-url +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/claude-code.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/claude-code.md new file mode 100644 index 000000000..7edd02da6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/claude-code.md @@ -0,0 +1,15 @@ +--- +title: Claude Code +weight: 5 +description: Configure Claude Code CLI to use agentgateway running in Kubernetes +test: + claude-code-k8s: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/integrations/llm-clients/claude-code.md + path: claude-code-k8s +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s/claude-code.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/continue.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/continue.md new file mode 100644 index 000000000..54f7f89bc --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/continue.md @@ -0,0 +1,7 @@ +--- +title: VS Code Continue +weight: 20 +description: Configure VS Code Continue to use agentgateway running in Kubernetes +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s/continue.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/curl.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/curl.md new file mode 100644 index 000000000..772ccb9b5 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/curl.md @@ -0,0 +1,7 @@ +--- +title: curl +weight: 40 +description: Test agentgateway running in Kubernetes with curl +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s/curl.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/cursor.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/cursor.md new file mode 100644 index 000000000..3704577d7 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/cursor.md @@ -0,0 +1,7 @@ +--- +title: Cursor +weight: 10 +description: Configure Cursor AI code editor to use agentgateway running in Kubernetes +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s/cursor.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/github-copilot.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/github-copilot.md new file mode 100644 index 000000000..0a2169f31 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/github-copilot.md @@ -0,0 +1,7 @@ +--- +title: GitHub Copilot +weight: 25 +description: Configure GitHub Copilot to use agentgateway running in Kubernetes +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s/github-copilot.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/openai-sdk.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/openai-sdk.md new file mode 100644 index 000000000..18870d413 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/openai-sdk.md @@ -0,0 +1,7 @@ +--- +title: OpenAI SDK +weight: 30 +description: Use the OpenAI Python and Node.js SDKs with agentgateway running in Kubernetes +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s/openai-sdk.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-clients/windsurf.md b/content/docs/kubernetes/1.1.x/integrations/llm-clients/windsurf.md new file mode 100644 index 000000000..203b85e29 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-clients/windsurf.md @@ -0,0 +1,7 @@ +--- +title: Windsurf +weight: 15 +description: Configure Windsurf AI code editor to use agentgateway running in Kubernetes +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/llm-clients-k8s/windsurf.md" >}} diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-observability/_index.md b/content/docs/kubernetes/1.1.x/integrations/llm-observability/_index.md new file mode 100644 index 000000000..ed57028ac --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-observability/_index.md @@ -0,0 +1,34 @@ +--- +title: LLM observability +weight: 10 +description: Prompt logging, cost tracking, and audit trail via Langfuse, LangSmith, and more +test: skip +--- + +Agentgateway can send LLM telemetry to specialized observability platforms for prompt logging (request/response logging), cost tracking, audit trail, and performance monitoring. + + +## How it works + +Agentgateway exports LLM telemetry via OpenTelemetry, which can be forwarded to LLM-specific observability platforms. These platforms provide the following. + +- **Prompt/response logging** - Full request and response capture (also known as request logging, audit trail). +- **Token usage tracking** - Monitor costs across models and users (also known as cost tracking, spend monitoring). +- **Latency analytics** - Track response times and identify bottlenecks. +- **Evaluation** - Score and evaluate LLM outputs. +- **Prompt management** - Version and manage prompts. + +## Configuration + +Set up OpenTelemetry tracing to export LLM-specific telemetry. See the [OpenTelemetry stack setup guide]({{< link-hextra path="/observability/otel-stack/" >}}) for details. + +Agentgateway automatically includes these LLM-specific trace attributes. + +| Attribute | Description | +|-----------|-------------| +| `gen_ai.operation.name` | Operation type (chat, completion, embedding). | +| `gen_ai.request.model` | Requested model name. | +| `gen_ai.response.model` | Actual model used. | +| `gen_ai.usage.input_tokens` | Input token count. | +| `gen_ai.usage.output_tokens` | Output token count. | +| `gen_ai.provider.name` | LLM provider (openai, anthropic, etc.). | diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-observability/langfuse.md b/content/docs/kubernetes/1.1.x/integrations/llm-observability/langfuse.md new file mode 100644 index 000000000..7325a9db9 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-observability/langfuse.md @@ -0,0 +1,195 @@ +--- +title: Langfuse +weight: 10 +description: Integrate agentgateway with Langfuse for LLM analytics and prompt management +--- + +[Langfuse](https://langfuse.com/) is an open-source LLM observability platform that provides prompt management, analytics, and evaluation. + +## Features + +- **Prompt tracing** - Log all prompts and responses. +- **Cost tracking** - Monitor token usage and costs. +- **Latency analytics** - Track response times. +- **Prompt management** - Version and deploy prompts. +- **Evaluation** - Score and evaluate outputs. +- **User tracking** - Attribute usage to users. + +## Setup + +### Self-hosted Langfuse + +Deploy Langfuse to your Kubernetes cluster. + +```bash +# Create namespace +kubectl create namespace langfuse + +# Deploy PostgreSQL +kubectl apply -f - <" + # Or export to self-hosted Langfuse + otlphttp/langfuse-local: + endpoint: http://langfuse.langfuse.svc.cluster.local:3000/api/public/otel + debug: + verbosity: detailed + service: + pipelines: + traces: + receivers: [otlp] + exporters: [debug, otlphttp/langfuse] # Use otlphttp/langfuse-local for self-hosted +EOF +``` + +{{< callout type="info" >}} +To create the base64-encoded credentials for Langfuse Cloud, run the following command. +```bash +echo -n "public-key:secret-key" | base64 +``` +{{< /callout >}} + +## Verify integration + +1. Send a request through agentgateway to an LLM backend. + ```bash + curl -X POST http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [{"role": "user", "content": "Hello!"}] + }' + ``` + +2. Navigate to your Langfuse dashboard and verify that the trace appears with the following information. + - Full prompt and response. + - Token counts (input and output). + - Model information. + - Latency metrics. + +## Learn more + +- [Langfuse Documentation](https://langfuse.com/docs) +- [OpenTelemetry stack setup]({{< link-hextra path="/observability/otel-stack/" >}}) +- [LLM observability metrics]({{< link-hextra path="/llm/observability/" >}}) diff --git a/content/docs/kubernetes/1.1.x/integrations/llm-observability/langsmith.md b/content/docs/kubernetes/1.1.x/integrations/llm-observability/langsmith.md new file mode 100644 index 000000000..3675aecfb --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/llm-observability/langsmith.md @@ -0,0 +1,96 @@ +--- +title: LangSmith +weight: 20 +description: Integrate agentgateway with LangSmith for LLM debugging and monitoring +--- + +[LangSmith](https://smith.langchain.com/) is LangChain's platform for debugging, testing, evaluating, and monitoring LLM applications. + +## Features + +- **Trace logging** - Detailed request/response logging. +- **Debugging** - Step-through debugging of LLM calls. +- **Evaluation** - Automated testing and evaluation. +- **Monitoring** - Production monitoring and alerting. +- **Datasets** - Build and manage evaluation datasets. + +## Setup + +1. Sign up at [smith.langchain.com](https://smith.langchain.com/). +2. Create a project and get your API key. +3. Create a Kubernetes secret with your API key. + +```bash +kubectl create secret generic langsmith-api-key \ + --from-literal=api-key=YOUR_LANGSMITH_API_KEY \ + -n telemetry +``` + +## Configuration + +Configure the OpenTelemetry Collector to forward traces to LangSmith. + +```yaml +# Update the traces collector +helm upgrade --install opentelemetry-collector-traces opentelemetry-collector \ + --repo https://open-telemetry.github.io/opentelemetry-helm-charts \ + --version 0.127.2 \ + --set mode=deployment \ + --set image.repository="otel/opentelemetry-collector-contrib" \ + --set command.name="otelcol-contrib" \ + --namespace=telemetry \ + --create-namespace \ + -f -<}}) +- [LLM observability metrics]({{< link-hextra path="/llm/observability/" >}}) diff --git a/content/docs/kubernetes/1.1.x/integrations/mcp-clients/_index.md b/content/docs/kubernetes/1.1.x/integrations/mcp-clients/_index.md new file mode 100644 index 000000000..f6b8af6a0 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/integrations/mcp-clients/_index.md @@ -0,0 +1,17 @@ +--- +title: MCP clients +weight: 15 +description: Connect AI coding assistants to MCP servers exposed through agentgateway on Kubernetes +test: + mcp-clients-k8s: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/mcp/static-mcp.md + path: setup-mcp-server + - file: content/docs/kubernetes/latest/integrations/mcp-clients/_index.md + path: mcp-clients-k8s +--- + +{{< reuse "agw-docs/pages/agentgateway/integrations/mcp-clients-k8s.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/_index.md b/content/docs/kubernetes/1.1.x/llm/_index.md new file mode 100644 index 000000000..27e794733 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/_index.md @@ -0,0 +1,12 @@ +--- +title: LLM consumption +weight: 20 +icon: psychology +description: +test: skip +--- + +Consume services from LLM providers. + + + diff --git a/content/docs/kubernetes/1.1.x/llm/about.md b/content/docs/kubernetes/1.1.x/llm/about.md new file mode 100644 index 000000000..e7d477068 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/about.md @@ -0,0 +1,9 @@ +--- +title: About +weight: 10 +description: +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/about.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/llm/alias.md b/content/docs/kubernetes/1.1.x/llm/alias.md new file mode 100644 index 000000000..767200a38 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/alias.md @@ -0,0 +1,7 @@ +--- +title: Model aliasing +weight: 20 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/alias.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/api-keys.md b/content/docs/kubernetes/1.1.x/llm/api-keys.md new file mode 100644 index 000000000..e7a7dffc0 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/api-keys.md @@ -0,0 +1,7 @@ +--- +title: API keys +weight: 25 +description: Manage API keys for LLM provider authentication. +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/api-keys.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/budget-limits.md b/content/docs/kubernetes/1.1.x/llm/budget-limits.md new file mode 100644 index 000000000..588308801 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/budget-limits.md @@ -0,0 +1,17 @@ +--- +title: Budget and spend limits +weight: 75 +description: Control LLM spending by enforcing token budget limits per API key or user. +test: + budget-limits-apikey-auth: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/llm/budget-limits.md + path: budget-limits +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/budget-limits.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/content-routing.md b/content/docs/kubernetes/1.1.x/llm/content-routing.md new file mode 100644 index 000000000..ca3d90e50 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/content-routing.md @@ -0,0 +1,15 @@ +--- +title: Content-based routing +weight: 45 +description: Route requests to different LLM backends based on request body content, such as the requested model name. +test: + content-routing-model: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/content-routing.md + path: content-routing +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/content-routing.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/cost-tracking.md b/content/docs/kubernetes/1.1.x/llm/cost-tracking.md new file mode 100644 index 000000000..9bd404b68 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/cost-tracking.md @@ -0,0 +1,7 @@ +--- +title: LLM cost tracking +weight: 85 +description: Track and monitor LLM costs per request using token usage metrics. +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/cost-tracking.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/failover.md b/content/docs/kubernetes/1.1.x/llm/failover.md new file mode 100644 index 000000000..b8c6becca --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/failover.md @@ -0,0 +1,17 @@ +--- +title: Model failover +weight: 40 +description: Priority-based failover across LLM providers (automatic fallback when models fail or are rate-limited). +test: + failover: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/httpbun.md + path: setup-httpbun-llm + - file: content/docs/kubernetes/latest/llm/failover.md + path: failover +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/failover.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/functions.md b/content/docs/kubernetes/1.1.x/llm/functions.md new file mode 100644 index 000000000..59587c85e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/functions.md @@ -0,0 +1,7 @@ +--- +title: Function calling +weight: 50 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/functions.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/_index.md b/content/docs/kubernetes/1.1.x/llm/guardrails/_index.md new file mode 100644 index 000000000..98a23ac37 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/_index.md @@ -0,0 +1,9 @@ +--- +title: Guardrails +weight: 55 +description: Protect LLM interactions with prompt guards that evaluate and filter requests and responses for harmful or policy-violating content. +test: skip +--- + +Guardrails are security policies that inspect LLM requests and responses to detect and block harmful, policy-violating, or inappropriate content before it reaches the model or the user. You can apply prompt guards to the request phase, the response phase, or both. + diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/bedrock-guardrails.md b/content/docs/kubernetes/1.1.x/llm/guardrails/bedrock-guardrails.md new file mode 100644 index 000000000..0d820040d --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/bedrock-guardrails.md @@ -0,0 +1,167 @@ +--- +title: AWS Bedrock Guardrails +weight: 20 +description: Apply AWS Bedrock Guardrails to filter LLM requests and responses for policy-violating content. +--- + +[AWS Bedrock Guardrails](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html) provide content filtering, PII detection, topic restrictions, and word filters. You must create the guardrail policies in the AWS console and then apply them to LLM route that you want to protect. When a request or response violates a guardrail policy, the agentgateway proxy blocks the interaction and returns an error. + +AWS Bedrock Guardrails are model-agnostic and can be applied to any Large Language Model (LLM), whether it is hosted on AWS Bedrock, another cloud provider (like Google or Azure), or on-premises. + +## Before you begin + +{{< reuse "agw-docs/snippets/agw-prereq-llm.md" >}} + +## Set up AWS Bedrock guardrails + +1. Create a guardrail in the [AWS console](https://console.aws.amazon.com/bedrock/home#/guardrails) or via the AWS CLI. +2. Retrieve your guardrail identifier and version. For more information, see the [AWS documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-components.html). + ```sh + aws bedrock list-guardrails --region + ``` + + Example output: + ```console + { + "guardrails": [ + { + "id": "a1aaaa11aa1a", + "arn": "arn:aws:bedrock:us-west-2:11111111111:guardrail/a1aaaa11aa1a", + "status": "READY", + "name": "my-guardrail", + "description": "Testing agentgateway bedrock guardrail integration ", + "version": "DRAFT", + "createdAt": "2026-02-09T17:59:29+00:00", + "updatedAt": "2026-02-09T18:01:29.567223+00:00" + } + ] + } + ``` + +3. Create a Kubernetes secret with your AWS credentials. Make sure that you have permission to invoke the Bedrock Guardrails API. + ```sh + kubectl create secret generic aws-secret \ + -n {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --from-literal=accessKey="$AWS_ACCESS_KEY_ID" \ + --from-literal=secretKey="$AWS_SECRET_ACCESS_KEY" \ + --from-literal=sessionToken="$AWS_SESSION_TOKEN" \ + --type=Opaque \ + --dry-run=client -o yaml | kubectl apply -f - + ``` + +4. Configure the prompt guard. Add the ID, version, and region of your guardrail. + ```yaml + kubectl apply -f - <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: openai-prompt-guard + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: openai + backend: + ai: + promptGuard: + request: + - bedrockGuardrails: + identifier: + version: "" + region: > + policies: + auth: + aws: + secretRef: + name: aws-secret + response: + - bedrockGuardrails: + identifier: + version: "" + region: > + policies: + auth: + aws: + secretRef: + name: aws-secret + EOF + ``` + + {{< callout type="info" >}} + The `aws: {}` configuration uses the default AWS credential chain (IAM role, environment variables, or instance profile). For authentication details, see the [AWS authentication documentation](https://docs.aws.amazon.com/sdk-for-go/api/aws/session/). + {{< /callout >}} + + +5. Test the guardrail. The following commands assume that you set up your guardrail to block requests that contain email information. + {{< tabs tabTotal="2" items="OpenAI v1/chat/completions, Custom route" >}} + {{% tab tabName="OpenAI v1/chat/completions" %}} + **Cloud Provider LoadBalancer**: + ```sh + curl "$INGRESS_GW_ADDRESS/v1/chat/completions" -H content-type:application/json -d '{ + "model": "", + "messages": [ + { + "role": "user", + "content": "My email is test@solo.io" + } + ] + }' | jq + ``` + + **Localhost**: + ```sh + curl "localhost:8080/v1/chat/completions" -H content-type:application/json -d '{ + "model": "", + "messages": [ + { + "role": "user", + "content": "My email is test@solo.io" + } + ] + }' | jq + ``` + {{% /tab %}} + {{% tab tabName="Custom route" %}} + **Cloud Provider LoadBalancer**: + ```sh + curl "$INGRESS_GW_ADDRESS/openai" -H content-type:application/json -d '{ + "model": "", + "messages": [ + { + "role": "user", + "content": "My email is test@solo.io" + } + ] + }' | jq + ``` + + **Localhost**: + ```sh + curl "localhost:8080/openai" -H content-type:application/json -d '{ + "model": "", + "messages": [ + { + "role": "user", + "content": "My email is test@solo.io" + } + ] + }' | jq + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ```console + The request was rejected due to inappropriate content + ``` + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} openai-prompt-guard -n {{< reuse "agw-docs/snippets/namespace.md" >}} +kubectl delete secret aws-secret -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/google-model-armor.md b/content/docs/kubernetes/1.1.x/llm/guardrails/google-model-armor.md new file mode 100644 index 000000000..6d635a25d --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/google-model-armor.md @@ -0,0 +1,271 @@ +--- +title: Google Model Armor +weight: 30 +description: Apply Google Cloud Model Armor templates to sanitize LLM requests and responses. +--- + +[Google Cloud Model Armor](https://cloud.google.com/security/products/model-armor) lets you create safety templates in the Google Cloud console and apply them to LLM traffic. Model Armor sanitizes both user prompts and model responses against your configured policies, blocking content that violates your templates. + +Google Cloud Model Armor guardrails are model-agnostic and can be applied to any Large Language Model (LLM), whether it is hosted on Google Cloud, another cloud provider (like Amazon or Azure), or on-premises. + +## Before you begin + +1. {{< reuse "agw-docs/snippets/prereq-agentgateway.md" >}} +2. Set up access to the [Gemini LLM provider]({{< link-hextra path="/llm/providers/gemini/" >}}). + +## Set up the Google Model Armor + +1. Log in to the Google Cloud console and create a [Model Armor template](https://console.cloud.google.com/security/model-armor). For more information, see the [Google Cloud documentation](https://docs.cloud.google.com/model-armor/overview). +2. Note the template ID, project ID, and the region where the template is deployed. Alternatively, you can use the following command to retrieve this information. + ```sh + gcloud model-armor templates list --location= + ``` + +## Configure access to GCP + +Set up your agentgateway proxy with the credentials so that you can access Google Model Armor guardrails. The setup varies depending on what type of cluster you run agentgateway in. + +{{< tabs tabTotal="2" items="Local cluster (kind), GKE cluster" >}} +{{% tab tabName="Local cluster (kind)" %}} + +Create a service account with the required permissions to access Google Model Armor. Then mount a JSON key file to the `agentgateway-proxy` pod. + +1. Set the Google Cloud project ID where you created the Google Model Armor template as an environment variable. + ```sh + export PROJECT_ID= + ``` + +2. Create a service account and assign permissions to access Google Model Armor. Then, create a local JSON key file with your credentials. + ```sh + gcloud iam service-accounts create agw-sa-modelarmor-kind --project $PROJECT_ID + + gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member="serviceAccount:agw-sa-modelarmor-kind@$PROJECT_ID.iam.gserviceaccount.com" \ + --role="roles/modelarmor.user" + + gcloud iam service-accounts keys create key.json \ + --iam-account=agw-sa-modelarmor-kind@$PROJECT_ID.iam.gserviceaccount.com + ``` + +3. Optional: Review the local JSON key file. + ```sh + cat ./key.json + ``` + +4. Create a Kubernetes secret to store the details of your JSON key file. + ```sh + kubectl create secret generic gcp-credentials \ + -n {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --from-file=key.json=./key.json + ``` + +5. Patch the `agentgateway-proxy` deployment to mount the secret as a volume. + ```sh + kubectl patch deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} --type=json -p='[ + { + "op": "add", + "path": "/spec/template/spec/volumes/-", + "value": { + "name": "gcp-credentials", + "secret": {"secretName": "gcp-credentials"} + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/volumeMounts/-", + "value": { + "name": "gcp-credentials", + "mountPath": "/var/secrets/google", + "readOnly": true + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env/-", + "value": { + "name": "GOOGLE_APPLICATION_CREDENTIALS", + "value": "/var/secrets/google/key.json" + } + } + ]' + ``` + +6. Verify that the agentgateway proxy picked up the credentials. + ```sh + kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} \ + -o jsonpath='{.spec.template.spec.containers[0].env}' | jq + ``` + + Example output: + ```console + ... + { + "name": "GOOGLE_APPLICATION_CREDENTIALS", + "value": "/var/secrets/google/key.json" + } + ... + ``` + +{{% /tab %}} +{{% tab tabName="GKE cluster" %}} + +In your GKE cluster, set up workload identity. + +1. Set the Google Cloud project ID where you created the Google Model Armor template as an environment variable. + ```sh + export PROJECT_ID= + export ZONE= + export CLUSTER= + ``` + +2. Enable workload identity on your cluster. + ```sh + gcloud container clusters update $CLUSTER \ + --workload-pool=$PROJECT_ID.svc.id.goog \ + --zone $ZONE + ``` + +3. Create a GCP service account with Model Armor permissions. + ```sh + gcloud iam service-accounts create agentgateway-sa \ + --project=$PROJECT_ID + + gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member="serviceAccount:agentgateway-sa@$PROJECT_ID.iam.gserviceaccount.com" \ + --role="roles/modelarmor.user" + ``` + +4. Allow the agentgateway proxy's Kubernetes service account to impersonate the GCP service account. + ```sh + gcloud iam service-accounts add-iam-policy-binding \ + agentgateway-sa@$PROJECT_ID.iam.gserviceaccount.com \ + --role="roles/iam.workloadIdentityUser" \ + --member="serviceAccount:$PROJECT_ID.svc.id.goog[{{< reuse "agw-docs/snippets/namespace.md" >}}/agentgateway-proxy]" + ``` + +5. Annotate the Kubernetes service account. + ```sh + kubectl annotate serviceaccount agentgateway-proxy \ + -n {{< reuse "agw-docs/snippets/namespace.md" >}} \ + iam.gke.io/gcp-service-account=agentgateway-sa@$PROJECT_ID.iam.gserviceaccount.com + ``` + +6. Restart the agentgateway proxy. + ```sh + kubectl rollout restart deploy/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} + ``` + +{{% /tab %}} +{{< /tabs >}} + +## Apply guardrails + +1. Configure the prompt guard and apply it to the Gemini route that you set up before you began. Add the location, project ID and template ID of your guardrail. + ```yaml + kubectl apply -f - <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: google-prompt-guard + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: google + backend: + ai: + promptGuard: + request: + - googleModelArmor: + templateId: + location: + policies: + auth: + gcp: + type: AccessToken + response: + - googleModelArmor: + templateId: + location: + policies: + auth: + gcp: + type: AccessToken + EOF + ``` + +2. Send a request to the Gemini provider that triggers the guardrail. + {{< tabs tabTotal="3" items="Gemini default, OpenAI-compatible v1/chat/completions, Custom route" >}} + {{% tab tabName="Gemini default" %}} + **Cloud Provider LoadBalancer**: + ```sh + curl "$INGRESS_GW_ADDRESS/v1beta/openai/chat/completions" -H content-type:application/json -d '{ + "model": "", + "messages": [ + {"role": "user", "content": "I want to harm myself"} + ] + }' + ``` + + **Localhost**: + ```sh + curl "localhost:8080/v1beta/openai/chat/completions" -H content-type:application/json -d '{ + "model": "", + "messages": [ + {"role": "user", "content": "I want to harm myself"} + ] + }' + ``` + {{% /tab %}} + {{% tab tabName="OpenAI-compatible v1/chat/completions" %}} + **Cloud Provider LoadBalancer**: + ```sh + curl "$INGRESS_GW_ADDRESS/v1/chat/completions" -H content-type:application/json -d '{ + "model": "", + "messages": [ + {"role": "user", "content": "I want to harm myself"} + ] + }' + ``` + + **Localhost**: + ```sh + curl "localhost:8080/v1/chat/completions" -H content-type:application/json -d '{ + "model": "", + "messages": [ + {"role": "user", "content": "I want to harm myself"} + ] + }' + ``` + {{% /tab %}} + {{% tab tabName="Custom route" %}} + **Cloud Provider LoadBalancer**: + ```sh + curl "$INGRESS_GW_ADDRESS/gemini" -H content-type:application/json -d '{ + "model": "", + "messages": [ + {"role": "user", "content": "I want to harm myself"} + ] + }' + ``` + + **Localhost**: + ```sh + curl "localhost:8080/gemini" -H content-type:application/json -d '{ + "model": "", + "messages": [ + {"role": "user", "content": "I want to harm myself"} + ] + }' + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ```console + The request was rejected due to inappropriate content + ``` + diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/moderation.md b/content/docs/kubernetes/1.1.x/llm/guardrails/moderation.md new file mode 100644 index 000000000..b84ce313b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/moderation.md @@ -0,0 +1,92 @@ +--- +title: OpenAI moderation +weight: 15 +description: Detects potentially harmful content across categories including hate, harassment, self-harm, sexual content, and violence with the OpenAI moderation API. +--- + +The OpenAI Moderation API detects potentially harmful content across categories including hate, harassment, self-harm, sexual content, and violence. + +## Before you begin + +{{< reuse "agw-docs/snippets/agw-prereq-llm.md" >}} + +### Block harmful content + +1. Configure the prompt guard to use OpenAI Moderation: + ```yaml + kubectl apply -f - <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: openai-prompt-guard + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: openai + backend: + ai: + promptGuard: + request: + - openAIModeration: + policies: + auth: + secretRef: + name: openai-secret + model: omni-moderation-latest + response: + message: "Content blocked by moderation policy" + EOF + ``` + +2. Test with content that triggers moderation. + {{< tabs tabTotal="2" items="Cloud Provider LoadBalancer,Port-forward for local testing" >}} + + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -i "$INGRESS_GW_ADDRESS/openai" \ + -H "content-type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [ + { + "role": "user", + "content": "I want to harm myself" + } + ] + }' + ``` + {{% /tab %}} + + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -i "localhost:8080/openai" \ + -H "content-type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [ + { + "role": "user", + "content": "I want to harm myself" + } + ] + }' + ``` + {{% /tab %}} + + {{< /tabs >}} + + Expected response: + ``` + HTTP/1.1 403 Forbidden + Content blocked by moderation policy + ``` + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} openai-prompt-guard -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/multi-layer.md b/content/docs/kubernetes/1.1.x/llm/guardrails/multi-layer.md new file mode 100644 index 000000000..b3b8d9fa4 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/multi-layer.md @@ -0,0 +1,64 @@ +--- +title: Multi-layered guardrails +weight: 60 +description: Run prompt guards in sequence, creating defense-in-depth protection. +--- + +You can configure multiple prompt guards that run in sequence, creating defense-in-depth protection. Guards are evaluated in the order they appear in the configuration. + +Example configuration that uses all three layers: + +```yaml +kubectl apply -f - <}} +kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} +metadata: + name: content-safety-layered + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: openai + backend: + ai: + promptGuard: + request: + # Layer 1: Fast regex check for known patterns + - regex: + builtins: + - Ssn + - CreditCard + - Email + action: Reject + response: + message: "Request contains PII and cannot be processed" + # Layer 2: OpenAI moderation for harmful content + - openAIModeration: + policies: + auth: + secretRef: + name: openai-secret + model: omni-moderation-latest + response: + message: "Content blocked by moderation policy" + # Layer 3: Custom webhook for domain-specific checks + - webhook: + backendRef: + kind: Service + name: content-safety-webhook + port: 8000 + response: + # Response guards run in same order + - regex: + builtins: + - Ssn + - CreditCard + action: Mask + - webhook: + backendRef: + kind: Service + name: content-safety-webhook + port: 8000 +EOF +``` diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/overview.md b/content/docs/kubernetes/1.1.x/llm/guardrails/overview.md new file mode 100644 index 000000000..33f9f8637 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/overview.md @@ -0,0 +1,110 @@ +--- +title: About guardrails +weight: 5 +description: Protect LLM requests and responses from sensitive data exposure and harmful content using layered content safety and guardrail controls (PII detection, DLP). +--- + +Protect LLM requests and responses from sensitive data exposure and harmful content using layered content safety controls. + +## About + +In agentgateway, you can use guardrails to help prevent sensitive information from reaching LLM providers and block harmful content in both requests and responses. Guardrails broadly cover a range of content safety techniques including personally identifiable information (PII) detection, PII sanitization, data loss prevention, prompt guards, and other guardrail features. + +You can layer multiple protection mechanisms to create comprehensive guardrail protection: +- **Regex-based filters**: Fast, deterministic matching for known patterns like credit cards, SSNs, emails, and custom patterns +- **External moderation**: Leverage built-in model moderation endpoints and cloud provider-specific guardrails for advanced content filtering +- **Custom webhooks**: Integrate your own guardrail logic for specialized requirements + +## How guardrails works + +Agentgateway checks for content safety in the request and response paths. You can configure multiple prompt guards that run in sequence, allowing you to combine different detection methods. + +```mermaid +sequenceDiagram + participant Client + participant Gateway as Agentgateway + participant Guard as Guardrail + participant LLM + + Client->>Gateway: Send prompt + Gateway->>Guard: 1. Regex check (fast) + Guard-->>Gateway: Pass/Reject/Mask + + alt Passed Regex + Gateway->>Guard: 2. External moderation (if configured) + Guard-->>Gateway: Pass/Reject/Mask + + alt Passed Moderation + Gateway->>Guard: 3. Custom webhook (if configured) + Guard-->>Gateway: Pass/Reject/Mask + + alt Passed All Guards + Gateway->>LLM: Forward sanitized request + LLM-->>Gateway: Generate response + Gateway->>Guard: Response guards + Guard-->>Gateway: Pass/Reject/Mask + Gateway-->>Client: Return sanitized response + end + end + else Rejected + Gateway-->>Client: Return rejection message + end +``` + +The diagram shows content flowing through multiple guard layers. Each layer can: +- **Pass**: Allow content to proceed to the next layer +- **Reject**: Block the request and return an error message +- **Mask**: Replace sensitive patterns with placeholders and continue + + +## Choosing the right approach + +Use this table to decide which guardrail layer to use for your requirements: + +| Requirement | Recommended Approach | Reason | +|-------------|---------------------|--------| +| Detect known PII formats (SSN, credit cards, emails) | Regex with builtins | Fast, deterministic, no external dependencies | +| Block hate speech, violence, harmful content | External moderation (OpenAI, Bedrock) | ML-based detection trained for content safety | +| Organization-specific restricted terms | Regex with custom patterns | Simple pattern matching for known strings | +| Named entity recognition (people, orgs, places) | Custom webhook | Requires NER models not available in built-in options | +| HIPAA, PCI-DSS, or other compliance requirements | Layered approach | Combine regex + external moderation + custom validation | +| Jailbreak - DAN & Role Hijacking | Regex with custom patterns | Pattern-match known jailbreak phrases and role-injection strings before they reach the LLM | +| Credentials & Secrets (API keys, tokens, passwords) | Regex with custom patterns | Deterministic pattern matching for structured credential formats with no external dependencies | +| System prompt extraction | Regex with custom patterns | Detect phrases that attempt to reveal or override system instructions before the request is forwarded | +| Encoding Evasion & Delimiter Injection | Regex with custom patterns | Match encoded or delimiter-based bypass patterns to block evasion attempts early in the pipeline | +| Integration with existing DLP tools | Custom webhook | Allows reuse of existing security infrastructure | +| Fastest performance with minimal latency | Regex only | No external API calls | +| Most comprehensive protection | All three layers | Defense-in-depth with multiple detection methods | + +## Performance considerations + +Each content safety layer adds latency to requests. Plan your configuration accordingly: + +- **Regex guards**: < 1ms per check, negligible latency impact +- **External moderation**: 50-200ms depending on provider and network latency +- **Custom webhooks**: Varies based on webhook implementation and location + +To optimize performance: +- Use regex for fast, deterministic checks before slower external checks +- Deploy webhook servers in the same region as agentgateway +- Configure appropriate timeouts for external moderation endpoints +- Consider request size limits to avoid processing very large prompts + +## Next steps + +Check out the following guides to build your guardrail system. + +{{< cards >}} + {{< card link="../regex" title="Regex filters" description="Use custom regex patterns and built-in PII detectors to filter LLM requests and responses." >}} + {{< card link="../moderation" title="OpenAI moderation" description="Use the OpenAI Moderation API to detect harmful content across categories including hate, harassment, and violence." >}} + {{< card link="../bedrock-guardrails" title="AWS Bedrock Guardrails" description="Apply AWS Bedrock Guardrails to filter LLM requests and responses for policy-violating content." >}} + {{< card link="../google-model-armor" title="Google Model Armor" description="Apply Google Cloud Model Armor templates to sanitize LLM requests and responses." >}} + {{< card path="/llm/guardrails/webhook/" title="Custom webhooks" description="Integrate your own content safety logic by forwarding requests and responses to a custom webhook." >}} + {{< card link="../multi-layer" title="Multi-layered guardrails" description="Run prompt guards in sequence, creating defense-in-depth protection." >}} +{{< /cards >}} + +To track guardrails and content safety, see the following guide. + +{{< cards >}} + {{< card link="../../observability" title="Observe LLM traffic" description="Track content safety metrics and blocked requests." >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/regex.md b/content/docs/kubernetes/1.1.x/llm/guardrails/regex.md new file mode 100644 index 000000000..5d1ec7e0c --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/regex.md @@ -0,0 +1,7 @@ +--- +title: Regex filters +weight: 10 +description: Use custom regex patterns and built-in PII detectors to filter LLM requests and responses. +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/prompt-guards.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/_index.md b/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/_index.md new file mode 100644 index 000000000..73c3a0bf1 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/_index.md @@ -0,0 +1,9 @@ +--- +title: Custom webhooks +weight: 45 +description: +test: skip +--- + +Use the Guardrail Webhook API to set up your own custom guardrail controls for {{< reuse "agw-docs/snippets/agentgateway.md" >}}. + diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/guardrails.md b/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/guardrails.md new file mode 100644 index 000000000..00540df5f --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/guardrails.md @@ -0,0 +1,7 @@ +--- +title: Guardrail webhook guide +weight: 10 +description: Use the Guardrail Webhook API to set up your own custom guardrail controls. +--- + +{{< reuse "agw-docs/pages/agentgateway/guardrails.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/openapi-spec.md b/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/openapi-spec.md new file mode 100644 index 000000000..ae88c72d9 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/guardrails/webhook/openapi-spec.md @@ -0,0 +1,9 @@ +--- +title: Webhook API reference +weight: 20 +description: OpenAPI reference docs for the Guardrail Webhook API for agentgateway. +--- + +Review the OpenAPI reference docs for the Guardrail Webhook API for {{< reuse "agw-docs/snippets/agentgateway.md" >}}. + +{{< openapi url="https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/refs/heads/main/ai-guardrail-webhook-server/docs/gloo-ai-gateway-guardrail-webhook-openapi.yaml" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/load-balancing.md b/content/docs/kubernetes/1.1.x/llm/load-balancing.md new file mode 100644 index 000000000..f31bb43f7 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/load-balancing.md @@ -0,0 +1,15 @@ +--- +title: Load balancing +weight: 35 +description: Distribute requests across multiple LLM providers automatically (Power of Two Choices, P2C). +test: + load-balancing-multi-provider: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/load-balancing.md + path: load-balancing +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/load-balancing.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/observability.md b/content/docs/kubernetes/1.1.x/llm/observability.md new file mode 100644 index 000000000..dcc9a2a88 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/observability.md @@ -0,0 +1,17 @@ +--- +title: Metrics and logs +weight: 95 +description: +test: + llm-observability: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/llm/observability.md + path: llm-observability +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/observability.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/prompt-enrichment.md b/content/docs/kubernetes/1.1.x/llm/prompt-enrichment.md new file mode 100644 index 000000000..c66ca13e4 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/prompt-enrichment.md @@ -0,0 +1,6 @@ +--- +title: Prompt enrichment +weight: 60 +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/prompt-enrichment.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/prompt-templates.md b/content/docs/kubernetes/1.1.x/llm/prompt-templates.md new file mode 100644 index 000000000..c1bbb2078 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/prompt-templates.md @@ -0,0 +1,300 @@ +--- +title: Prompt templates +weight: 65 +description: Use static and dynamic prompt templates to customize LLM requests. +--- + +Use prompt templates to inject dynamic context, user identity, or other runtime information into your LLM prompts. Agentgateway supports both static template patterns (prepend/append) and dynamic variable-based templating using CEL expressions. + +## About prompt templates + +Prompt templates allow you to standardize prompts across your organization with consistent instructions, inject dynamic context such as user identity or JWT claims, customize behavior per user, and add metadata like request IDs for tracking. + +Unlike simple `{{variable}}` substitution systems, agentgateway uses [CEL (Common Expression Language)](https://agentgateway.dev/docs/standalone/latest/reference/cel/) expressions. This gives you full expression logic including conditionals, functions, and complex transformations. + +## Templating approaches + +Agentgateway provides two complementary approaches to prompt templating. + +| Approach | Use Case | Example | +|----------|----------|---------| +| Static templates | Fixed prompts that apply to all requests | "Answer all questions in French." | +| Dynamic templates | Variable injection from JWT claims, headers, or request context | "You are assisting user `{jwt.sub}` from organization `{jwt.org}`." | + +You can use these approaches individually or combine them for maximum flexibility. + +## Before you begin + +{{< reuse "agw-docs/snippets/agw-prereq-llm.md" >}} + +## Static prompt templates + +Static templates use prompt enrichment to prepend or append fixed messages to every request. This is ideal for setting consistent behavior guidelines, adding organizational policies, or defining output formats. + +1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource with static prompt enrichment. + + ```yaml + kubectl apply -f- <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: static-prompt-template + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: openai + backend: + ai: + prompt: + prepend: + - role: system + content: "You are a helpful customer service assistant. Always be polite and professional." + append: + - role: system + content: "If you cannot answer a question, say so clearly rather than making up information." + EOF + ``` + +2. Send a request without system prompts. The static template is automatically applied. + + {{< tabs tabTotal="2" items="Cloud Provider LoadBalancer,Port-forward for local testing" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl "$INGRESS_GW_ADDRESS/openai" -H content-type:application/json -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "user", + "content": "How do I return a product?" + } + ] + }' | jq -r '.choices[].message.content' + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl "localhost:8080/openai" -H content-type:application/json -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "user", + "content": "How do I return a product?" + } + ] + }' | jq -r '.choices[].message.content' + ``` + {{% /tab %}} + {{< /tabs >}} + + The response follows the prepended and appended guidelines even though they were not in the original request. + +## Dynamic prompt templates + +Dynamic templates use CEL transformations to inject variables from the request context into prompts. This is ideal for personalizing prompts with user identity, adding request metadata, or applying conditional prompt modification based on headers or claims. + +{{< callout type="info" >}} +JWT claims in transformations require JWT authentication to be configured. See the [JWT authentication guide]({{< link-hextra path="/security/jwt/">}}) for setup instructions. +{{< /callout >}} + +### Inject user identity from headers + +1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource that injects user identity from request headers. + + ```yaml + kubectl apply -f- <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: dynamic-prompt-template + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: openai + traffic: + transformation: + request: + body: | + json(request.body).with(body, + { + "model": body.model, + "messages": [{"role": "system", "content": "You are assisting user: " + default(request.headers["x-user-id"], "anonymous")}] + + body.messages + } + ).toJson() + EOF + ``` + +2. Send a request with a user ID header. + + {{< tabs tabTotal="2" items="Cloud Provider LoadBalancer,Port-forward for local testing" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl "$INGRESS_GW_ADDRESS/openai" -H content-type:application/json -H "x-user-id: alice" -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "user", + "content": "What are my recent orders?" + } + ] + }' | jq -r '.choices[].message.content' + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl "localhost:8080/openai" -H content-type:application/json -H "x-user-id: alice" -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "user", + "content": "What are my recent orders?" + } + ] + }' | jq -r '.choices[].message.content' + ``` + {{% /tab %}} + {{< /tabs >}} + + The request body includes a system message: `"You are assisting user: alice"`. + +### Available CEL variables for templating + +You can use these variables in your CEL transformation expressions. + +| Variable | Description | Example | +|----------|-------------|---------| +| `request.headers["name"]` | Request header values | `request.headers["x-user-id"]` | +| `request.path` | Request path | `request.path` returns `/openai` | +| `request.method` | HTTP method | `request.method` returns `POST` | +| `jwt.sub` | JWT subject claim | `jwt.sub` returns `"user123"` | +| `jwt.iss` | JWT issuer claim | `jwt.iss` returns `"https://auth.example.com"` | +| `jwt.aud` | JWT audience claim | `jwt.aud` returns `"api://myapp"` | +| `jwt['custom-claim']` | Custom JWT claims | `jwt['org-id']` returns custom claim value | + +For a complete list of available variables and functions, see the [CEL reference documentation](https://agentgateway.dev/docs/standalone/latest/reference/cel/). + +## Common templating patterns + +### User context from JWT claims + +{{< callout type="warning" >}} +JWT claims are not currently available in CEL transformations when using `mcpAuthentication`. This is tracked in [agentgateway issue #870](https://github.com/agentgateway/agentgateway/issues/870). Use `jwtAuthentication` in the traffic policy instead. +{{< /callout >}} + +Inject user identity and organization from JWT claims into the prompt. + +```yaml +traffic: + transformation: + request: + body: | + json(request.body).with(body, + { + "model": body.model, + "messages": [ + { + "role": "system", + "content": "You are assisting " + jwt.sub + " from organization " + jwt['org-id'] + ". Tailor responses to their role: " + default(jwt.role, "user") + "." + } + ] + body.messages + } + ).toJson() +``` + +### Conditional templates based on headers + +Route premium users to enhanced instructions. + +```yaml +traffic: + transformation: + request: + body: | + json(request.body).with(body, + request.headers["x-user-tier"] == "premium" ? + { + "model": body.model, + "messages": [{"role": "system", "content": "Provide detailed, comprehensive answers with examples."}] + body.messages + } : + { + "model": body.model, + "messages": [{"role": "system", "content": "Provide concise, brief answers."}] + body.messages + } + ).toJson() +``` + +### Add request tracking metadata + +Inject request ID and timestamp for debugging. + +```yaml +traffic: + transformation: + request: + body: | + json(request.body).with(body, + { + "model": body.model, + "messages": [ + { + "role": "system", + "content": "Request ID: " + uuid() + " | Timestamp: " + string(request.startTime) + } + ] + body.messages + } + ).toJson() +``` + +### Combine static and dynamic templates + +Use prompt enrichment for static guidelines and transformations for dynamic context. + +```yaml +# Static guidelines via prompt enrichment +backend: + ai: + prompt: + prepend: + - role: system + content: "You are a helpful assistant. Always be polite." + append: + - role: system + content: "If uncertain, say so clearly." + +# Dynamic user context via transformation +traffic: + transformation: + request: + body: | + json(request.body).with(body, + { + "model": body.model, + "messages": body.messages + [ + { + "role": "system", + "content": "User context: " + default(request.headers["x-user-id"], "anonymous") + } + ] + } + ).toJson() +``` + +This applies both static prompts (prepend/append) and dynamic user context (from headers). + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} static-prompt-template dynamic-prompt-template -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +## Next steps + +- Learn about [CEL expressions](https://agentgateway.dev/docs/standalone/latest/reference/cel/) for advanced templating. +- Explore [transformations]({{< link-hextra path="/traffic-management/transformations/">}}) for request/response modification. +- Set up [JWT authentication]({{< link-hextra path="/security/jwt/">}}) to use JWT claims in templates. diff --git a/content/docs/kubernetes/1.1.x/llm/providers/_index.md b/content/docs/kubernetes/1.1.x/llm/providers/_index.md new file mode 100644 index 000000000..010541e41 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/_index.md @@ -0,0 +1,8 @@ +--- +title: Providers +weight: 15 +description: +test: skip +--- + +Learn how to configure agentgateway for a particular LLM. diff --git a/content/docs/kubernetes/1.1.x/llm/providers/anthropic.md b/content/docs/kubernetes/1.1.x/llm/providers/anthropic.md new file mode 100644 index 000000000..8b4eddb18 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/anthropic.md @@ -0,0 +1,8 @@ +--- +title: Anthropic +weight: 20 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/anthropic.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/llm/providers/azure.md b/content/docs/kubernetes/1.1.x/llm/providers/azure.md new file mode 100644 index 000000000..acd660f93 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/azure.md @@ -0,0 +1,8 @@ +--- +title: Azure +weight: 20 +description: Configuration and setup for Azure AI services provider +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/azure.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/llm/providers/bedrock.md b/content/docs/kubernetes/1.1.x/llm/providers/bedrock.md new file mode 100644 index 000000000..498334728 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/bedrock.md @@ -0,0 +1,7 @@ +--- +title: Amazon Bedrock +weight: 20 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/bedrock.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/providers/gemini.md b/content/docs/kubernetes/1.1.x/llm/providers/gemini.md new file mode 100644 index 000000000..c59c5a049 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/gemini.md @@ -0,0 +1,7 @@ +--- +title: Gemini +weight: 20 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/gemini.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/providers/httpbun.md b/content/docs/kubernetes/1.1.x/llm/providers/httpbun.md new file mode 100644 index 000000000..5f2e08c72 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/httpbun.md @@ -0,0 +1,16 @@ +--- +title: Mock LLM with httpbun +weight: 100 +description: +test: + setup-httpbun-llm: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/httpbun.md + path: setup-httpbun-llm +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/httpbun.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/llm/providers/multiple-endpoints.md b/content/docs/kubernetes/1.1.x/llm/providers/multiple-endpoints.md new file mode 100644 index 000000000..266807669 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/multiple-endpoints.md @@ -0,0 +1,8 @@ +--- +title: Multiple endpoints +weight: 40 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/multiple-endpoints.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/llm/providers/ollama.md b/content/docs/kubernetes/1.1.x/llm/providers/ollama.md new file mode 100644 index 000000000..90858eeb9 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/ollama.md @@ -0,0 +1,15 @@ +--- +title: Ollama +weight: 25 +description: Configure agentgateway to route LLM traffic to Ollama for local model inference +test: + ollama-provider-setup: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/ollama.md + path: ollama-provider-setup +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/ollama.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/providers/openai-compatible.md b/content/docs/kubernetes/1.1.x/llm/providers/openai-compatible.md new file mode 100644 index 000000000..b4e85d48e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/openai-compatible.md @@ -0,0 +1,7 @@ +--- +title: OpenAI-compatible providers +weight: 20 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/openai-compatible.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/providers/openai.md b/content/docs/kubernetes/1.1.x/llm/providers/openai.md new file mode 100644 index 000000000..3419a2dc1 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/openai.md @@ -0,0 +1,15 @@ +--- +title: OpenAI +weight: 20 +description: +test: + openai-setup: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/openai.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/providers/vertex.md b/content/docs/kubernetes/1.1.x/llm/providers/vertex.md new file mode 100644 index 000000000..23cb11af8 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/vertex.md @@ -0,0 +1,8 @@ +--- +title: Vertex AI +weight: 20 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/vertex.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/llm/providers/vllm.md b/content/docs/kubernetes/1.1.x/llm/providers/vllm.md new file mode 100644 index 000000000..6250423f0 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/providers/vllm.md @@ -0,0 +1,15 @@ +--- +title: vLLM +weight: 30 +description: Configure agentgateway to route traffic to vLLM for self-hosted model inference +test: + vllm-provider-setup: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/vllm.md + path: vllm-provider-setup +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/providers/vllm.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/rate-limit.md b/content/docs/kubernetes/1.1.x/llm/rate-limit.md new file mode 100644 index 000000000..bcaf43e16 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/rate-limit.md @@ -0,0 +1,17 @@ +--- +title: Rate limiting for LLMs +weight: 80 +description: Control LLM costs with token-based rate limiting and request-based limits. +test: + llm-token-rate-limit: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/httpbun.md + path: setup-httpbun-llm + - file: content/docs/kubernetes/latest/llm/rate-limit.md + path: llm-token-rate-limit +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/rate-limit.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/rbac.md b/content/docs/kubernetes/1.1.x/llm/rbac.md new file mode 100644 index 000000000..2e1d544b8 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/rbac.md @@ -0,0 +1,7 @@ +--- +title: CEL-based RBAC +weight: 90 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/rbac.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/realtime.md b/content/docs/kubernetes/1.1.x/llm/realtime.md new file mode 100644 index 000000000..7f35fad4d --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/realtime.md @@ -0,0 +1,17 @@ +--- +title: OpenAI Realtime +weight: 47 +description: Proxy OpenAI Realtime API WebSocket traffic and track token usage. +test: + realtime: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/llm/realtime.md + path: realtime +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/realtime.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/streaming.md b/content/docs/kubernetes/1.1.x/llm/streaming.md new file mode 100644 index 000000000..173d51a7e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/streaming.md @@ -0,0 +1,211 @@ +--- +title: Streaming +weight: 45 +description: Stream responses from the LLM to the end user through agentgateway. +test: + streaming-openai: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/llm/streaming.md + path: streaming-openai +--- + +Models return a response in two main ways: all at once in a single chunk, or in a stream of chunks. + +Click through the following tabs to see the request flows for each. + + + +{{< tabs tabTotal="2" items="Single response,Streaming">}} +{{% tab tabName="Single response" %}} + +{{< reuse-image src="/img/aig-streaming-false-light.svg" width="600px" alt="Figure: Response without streaming in a single chunk" caption="Figure: Response without streaming in a single chunk">}} + +{{< reuse-image-dark srcDark="/img/aig-streaming-false-dark.svg" width="600px" alt="Figure: Response without streaming in a single chunk" caption="Figure: Response without streaming in a single chunk">}} + +1. The client sends a request without streaming. +2. The gateway receives the request and forwards it to the LLM. +3. The LLM processes the request and generates a response, such as `"Hello world"`. +4. The gateway forwards the response in a single chunk to the client. + +{{% /tab %}} +{{% tab tabName="Streaming" %}} + +{{< reuse-image src="/img/aig-streaming-true-light.svg" width="600px" alt="Figure: Response in a stream of chunks" caption="Figure: Response in a stream of chunks">}} +{{< reuse-image-dark srcDark="/img/aig-streaming-true-dark.svg" width="600px" alt="Figure: Response in a stream of chunks" caption="Figure: Response in a stream of chunks">}} + +1. The client sends a request with streaming enabled. +2. The gateway receives the request and forwards it to the LLM. +3. The LLM processes the request and generates the first chunk of a response, such as `"Hello"`. +4. The gateway forwards the first chunk to the client. +5. The LLM continues to generate the next chunk of the response, such as `" world"`. +6. The gateway forwards the second chunk to the client. +7. The streaming process repeats until the entire response is generated and returned to the client. + +{{% /tab %}} +{{< /tabs >}} + +### Streaming benefits {#benefits} + +Streaming is useful for: + +- Large responses that take a long time to generate. This way, you avoid a lag that could impact the user experience or even trigger a timeout that interrupts the response generation process. +- Responses that are better received in smaller chunks, such as logging to troubleshoot or diagnose issues later. +- Interactive, chat-style applications where you want to see the response in real time. + +With {{< reuse "agw-docs/snippets/agentgateway.md" >}}, you can still apply policies to your streaming responses, such as prompt guards, JWT auth, and rate limiting. + +## Provider differences {#provider-differences} + +The streaming process differs for each LLM provider. + +### Request parameter for OpenAI, Azure, and Anthropic {#request-parameter} + +OpenAI, Azure, and Anthropic support streaming responses through Server-Sent Events (SSE). Note that Anthropic allows for more granular events such as `message_start` and `content_block_start`. + +In the body of your request to the LLM, include the `stream` parameter, such as in the following example: + +```json +'{ + "stream": true, + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "system", + "content": "You are a skilled developer who is good at explaining basic programming concepts to beginners." + }, + { + "role": "user", + "content": "In a couple words, tell me what I should call my first GitHub repo." + } + ] + }' +``` + +For more information, see the LLM provider docs: + +- [OpenAI](https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat#enable-streaming) +- [Anthropic](https://platform.claude.com/docs/en/build-with-claude/streaming) + + +### TrafficPolicy for Gemini, Vertex {#policy} + +Google uses an HTTP stream protocol which requires special handling. {{< reuse "agw-docs/snippets/agentgateway.md" >}} automatically handles this for Gemini and Vertex when you configure the route type with a {{< reuse "agw-docs/snippets/trafficpolicy.md" >}}. + +In the {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} for the HTTPRoute to the LLM provider, set the `routeType` option to `CHAT_STREAMING`, such as the following example: + +```yaml +apiVersion: {{< reuse "agw-docs/snippets/trafficpolicy-apiversion.md" >}} +kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} +metadata: + name: gemini-opt + namespace: default +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: gemini + ai: + routeType: CHAT_STREAMING +``` + +For more information, see the LLM provider docs: + +- [Gemini](https://ai.google.dev/gemini-api/docs/text-generation#streaming-responses) +- [Vertex](https://docs.cloud.google.com/vertex-ai/docs/reference/rest/v1/projects.locations.endpoints/serverStreamingPredict) + +## Streaming example {#example} + +The following steps show how to stream a response from OpenAI. + +### Before you begin + +1. {{< reuse "agw-docs/snippets/prereq-agentgateway.md" >}} +2. [Set up access to the OpenAI LLM provider]({{< link-hextra path="/llm/providers/openai/" >}}). + +### Stream a response from OpenAI {#openai} + +1. Send a request to the OpenAI provider that includes the streaming parameter `"stream": "true"`. For other providers, see [Provider differences](#provider-differences). + + {{< tabs tabTotal="2" items="Cloud Provider LoadBalancer,Port-forward for local testing" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh {paths="streaming-openai"} + curl "http://${INGRESS_GW_ADDRESS}/openai" -H content-type:application/json -d '{ + "stream": true, + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "system", + "content": "You are a skilled developer who is good at explaining basic programming concepts to beginners." + }, + { + "role": "user", + "content": "In a couple words, tell me what I should call my first GitHub repo." + } + ] + }' + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl "localhost:8080/openai" -H content-type:application/json -d '{ + "stream": true, + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "system", + "content": "You are a skilled developer who is good at explaining basic programming concepts to beginners." + }, + { + "role": "user", + "content": "In a couple words, tell me what I should call my first GitHub repo." + } + ] + }' + ``` + {{% /tab %}} + {{< /tabs >}} + +2. In the output, verify that the request succeeds and that you get back a streamed response from the chat completion API. + + ```console + data: {"id":"chatcmpl-BKq9o...","object":"chat.completion.chunk","created":1744306752,"model":"gpt-3.5-turbo-0125","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-BKq9o...","object":"chat.completion.chunk","created":1744306752,"model":"gpt-3.5-turbo-0125","choices":[{"index":0,"delta":{"content":"You"},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-BKq9o...","object":"chat.completion.chunk","created":1744306752,"model":"gpt-3.5-turbo-0125","choices":[{"index":0,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}]} + + data: {"id":"chatcmpl-BKq9o...","object":"chat.completion.chunk","created":1744306752,"model":"gpt-3.5-turbo-0125","choices":[{"index":0,"delta":{"content":" call"},"logprobs":null,"finish_reason":null}]} + ... + data: {"id":"chatcmpl-BKq9o...","object":"chat.completion.chunk","created":1744306752,"model":"gpt-3.5-turbo-0125","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + + data: [DONE] + ``` + + If you string together the `{"content":...}` chunks, you get the complete response. The `[DONE]` message indicates that the streaming process is complete. + +{{< doc-test paths="streaming-openai" >}} +# Verify streaming response contains SSE chunks and terminates with [DONE] +STREAM_RESPONSE=$(curl -s "http://${INGRESS_GW_ADDRESS}/openai" -H content-type:application/json -d '{ + "stream": true, + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "user", + "content": "Say hello in one word" + } + ] +}') + +# Verify the response contains streaming chunk markers +echo "$STREAM_RESPONSE" | grep -q "chat.completion.chunk" || { echo "FAIL: Response does not contain streaming chunks"; exit 1; } +echo "PASS: Response contains streaming chunks" + +# Verify the stream terminates with [DONE] +echo "$STREAM_RESPONSE" | grep -q '\[DONE\]' || { echo "FAIL: Response does not end with [DONE]"; exit 1; } +echo "PASS: Stream terminates with [DONE]" +{{< /doc-test >}} diff --git a/content/docs/kubernetes/1.1.x/llm/tracing.txt b/content/docs/kubernetes/1.1.x/llm/tracing.txt new file mode 100644 index 000000000..17cc51078 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/tracing.txt @@ -0,0 +1,7 @@ +--- +title: View traces +weight: 100 +description: +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/tracing.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/llm/transformations.md b/content/docs/kubernetes/1.1.x/llm/transformations.md new file mode 100644 index 000000000..16a56754c --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/transformations.md @@ -0,0 +1,26 @@ +--- +title: Request transformations +weight: 70 +description: Dynamically compute and set LLM request fields using CEL expressions. +test: + llm-transformations: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/llm/transformations.md + path: llm-transformations + llm-model-headers: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/llm/transformations.md + path: llm-model-headers +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/transformations.md" >}} diff --git a/content/docs/kubernetes/1.1.x/llm/virtual-keys.md b/content/docs/kubernetes/1.1.x/llm/virtual-keys.md new file mode 100644 index 000000000..316bffa68 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/llm/virtual-keys.md @@ -0,0 +1,19 @@ +--- +title: Virtual keys +weight: 30 +description: Issue API keys with per-key token budgets and cost tracking (also known as virtual keys). +test: + virtual-keys-auth: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/httpbun.md + path: setup-httpbun-llm + - file: content/docs/kubernetes/latest/llm/virtual-keys.md + path: virtual-keys + - file: content/docs/kubernetes/latest/llm/virtual-keys.md + path: virtual-keys-httpbun-test +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/virtual-keys.md" >}} diff --git a/content/docs/kubernetes/1.1.x/mcp/_index.md b/content/docs/kubernetes/1.1.x/mcp/_index.md new file mode 100644 index 000000000..ab40ab7c2 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/_index.md @@ -0,0 +1,8 @@ +--- +title: MCP connectivity +weight: 40 +icon: extension +test: skip +--- + +Route to Model Context Protocol (MCP) servers through {{< reuse "agw-docs/snippets/agentgateway.md" >}}. diff --git a/content/docs/kubernetes/1.1.x/mcp/about.md b/content/docs/kubernetes/1.1.x/mcp/about.md new file mode 100644 index 000000000..3bb2932ed --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/about.md @@ -0,0 +1,8 @@ +--- +title: About +weight: 5 +description: +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/about.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/mcp/auth/_index.md b/content/docs/kubernetes/1.1.x/mcp/auth/_index.md new file mode 100644 index 000000000..c8654c683 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/auth/_index.md @@ -0,0 +1,8 @@ +--- +title: MCP auth +weight: 50 +test: skip +--- + +Authenticate MCP clients with your MCP server by using MCP auth. + diff --git a/content/docs/kubernetes/1.1.x/mcp/auth/about.md b/content/docs/kubernetes/1.1.x/mcp/auth/about.md new file mode 100644 index 000000000..0e056cac7 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/auth/about.md @@ -0,0 +1,7 @@ +--- +title: About MCP auth +weight: 10 +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/mcp-auth-about.md" >}} diff --git a/content/docs/kubernetes/1.1.x/mcp/auth/keycloak.md b/content/docs/kubernetes/1.1.x/mcp/auth/keycloak.md new file mode 100644 index 000000000..c7ada0b06 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/auth/keycloak.md @@ -0,0 +1,12 @@ +--- +title: Set up Keycloak +weight: 20 +test: + setup-keycloak: + - file: content/docs/kubernetes/latest/install/helm.md + path: experimental + - file: content/docs/kubernetes/latest/mcp/auth/keycloak.md + path: setup-keycloak +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/mcp-auth-keycloak.md" >}} diff --git a/content/docs/kubernetes/1.1.x/mcp/auth/setup.md b/content/docs/kubernetes/1.1.x/mcp/auth/setup.md new file mode 100644 index 000000000..59767534e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/auth/setup.md @@ -0,0 +1,18 @@ +--- +title: Set up MCP auth +weight: 40 +test: + mcp-auth-setup: + - file: content/docs/kubernetes/latest/install/helm.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/mcp/static-mcp.md + path: setup-mcp-server + - file: content/docs/kubernetes/latest/mcp/auth/keycloak.md + path: setup-keycloak + - file: content/docs/kubernetes/latest/mcp/auth/setup.md + path: mcp-auth-setup +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/mcp-auth-setup.md" >}} diff --git a/content/docs/kubernetes/1.1.x/mcp/dynamic-mcp.md b/content/docs/kubernetes/1.1.x/mcp/dynamic-mcp.md new file mode 100644 index 000000000..58917d722 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/dynamic-mcp.md @@ -0,0 +1,14 @@ +--- +title: Dynamic MCP +weight: 20 +test: + dynamic-mcp: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/mcp/dynamic-mcp.md + path: dynamic-mcp +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/dynamic.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/mcp/https.md b/content/docs/kubernetes/1.1.x/mcp/https.md new file mode 100644 index 000000000..06a1c09a9 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/https.md @@ -0,0 +1,9 @@ +--- +title: Connect via HTTPS +weight: 40 +description: +--- + +Configure your agentgateway proxy to connect to an MCP server by using the HTTPS protocol. + +{{< reuse "agw-docs/pages/agentgateway/mcp/https.md">}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/mcp/mcp-access.md b/content/docs/kubernetes/1.1.x/mcp/mcp-access.md new file mode 100644 index 000000000..64eebd1f4 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/mcp-access.md @@ -0,0 +1,6 @@ +--- +title: JWT auth for services +weight: 55 +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/mcp-access.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/mcp/rate-limit.md b/content/docs/kubernetes/1.1.x/mcp/rate-limit.md new file mode 100644 index 000000000..fb7cb3b5d --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/rate-limit.md @@ -0,0 +1,17 @@ +--- +title: Rate limiting for MCP +weight: 65 +description: Control MCP tool call rates to prevent overload and ensure fair access to expensive tools. +test: + mcp-local-rate-limit: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/mcp/static-mcp.md + path: setup-mcp-server + - file: content/docs/kubernetes/latest/mcp/rate-limit.md + path: mcp-local-rate-limit +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/rate-limit.md" >}} diff --git a/content/docs/kubernetes/1.1.x/mcp/session.md b/content/docs/kubernetes/1.1.x/mcp/session.md new file mode 100644 index 000000000..7123d5208 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/session.md @@ -0,0 +1,7 @@ +--- +title: Stateful MCP +weight: 70 +description: Configure stateful or stateless session routing to MCP backends. +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/session.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/mcp/static-mcp.md b/content/docs/kubernetes/1.1.x/mcp/static-mcp.md new file mode 100644 index 000000000..0a12f288b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/static-mcp.md @@ -0,0 +1,14 @@ +--- +title: Static MCP +weight: 10 +test: + setup-mcp-server: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/mcp/static-mcp.md + path: setup-mcp-server +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/static.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/mcp/tool-access.md b/content/docs/kubernetes/1.1.x/mcp/tool-access.md new file mode 100644 index 000000000..396e1e467 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/tool-access.md @@ -0,0 +1,6 @@ +--- +title: Tool access +weight: 60 +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/tool-access.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/mcp/virtual.md b/content/docs/kubernetes/1.1.x/mcp/virtual.md new file mode 100644 index 000000000..fad2bc273 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/mcp/virtual.md @@ -0,0 +1,14 @@ +--- +title: Virtual MCP +weight: 30 +test: + virtual-mcp: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/mcp/virtual.md + path: virtual-mcp +--- + +{{< reuse "agw-docs/pages/agentgateway/mcp/multiplex.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/migrate/_index.md b/content/docs/kubernetes/1.1.x/migrate/_index.md new file mode 100644 index 000000000..c9b32be9f --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/_index.md @@ -0,0 +1,74 @@ +--- +title: "Migrate from Ingress" +weight: 900 +icon: swap_horiz +test: skip +--- + +## Introduction + +Welcome to the documentation for migrating from Ingress to **Gateway API** and **agentgateway**. Migration is supported using the +[kgateway ingress2gateway](https://github.com/kgateway-dev/ingress2gateway) tool, which is a fork of +[Kubernetes ingress2gateway](https://github.com/kubernetes-sigs/ingress2gateway) with the following additional features: + +- **Expanded Ingress NGINX Support:** Converts a wide range of Ingress NGINX-specific annotations, e.g. authentication, + rate limiting, CORS, backend TLS, etc. +- **Agentgateway Emitter:** Generates Gateway API and agentgateway-specific resources, e.g. AgentgatewayPolicy (traffic and backend behavior). + +[This upstream issue](https://github.com/kgateway-dev/ingress2gateway/issues/54) tracks merging these features into the Kubernetes ingress2gateway project. + +## Prerequisites + +Before you start the migration, ensure you have the following: + +1. **Agentgateway installed**: You need agentgateway running in the Kubernetes cluster containing the Ingresses to migrate. +2. **Kubernetes Cluster Access**: Ensure you have access to your Kubernetes cluster and necessary permissions to manage resources. + +## Installation + +The ingress2gateway tool can be installed on a variety of Linux platforms, macOS and Windows. Select your operating system below. + +- [Install ingress2gateway on macOS]({{< relref "install/macos/_index.md" >}}) +- [Install ingress2gateway on Linux]({{< relref "install/linux/_index.md" >}}) +- [Install ingress2gateway on Windows]({{< relref "install/windows/_index.md" >}}) + +## Example Conversions + +The following examples demonstrate how to use `ingress2gateway` to convert various Ingress resources to Gateway API and agentgateway resources. + +- [Basic Ingress]({{< relref "examples/basic.md" >}}) +- [Rate Limiting]({{< relref "examples/rate-limiting.md" >}}) +- [CORS]({{< relref "examples/cors.md" >}}) +- [SSL Redirect]({{< relref "examples/ssl-redirect.md" >}}) +- [External Auth]({{< relref "examples/external-auth.md" >}}) +- [Canary Release]({{< relref "examples/canary.md" >}}) +- [Backend TLS]({{< relref "examples/backend-tls.md" >}}) + +**Optional:** Use the Ingress NGINX [quickstart guide](https://kubernetes.github.io/ingress-nginx/deploy/#quick-start) to test connectivity +of the Ingress before converting. + +## Common workflows + +1. Convert a file and write output to a directory. + + ```bash + ingress2gateway print --providers=ingress-nginx --emitter=agentgateway --input-file ./ingress.yaml --output-dir ./out + ``` + +2. Convert a folder of YAML files. + + ```bash + ingress2gateway print --providers=ingress-nginx --emitter=agentgateway --input-dir ./manifests --output-dir ./out + ``` + +3. Check the tool version. + + ```bash + ingress2gateway version + ``` + +## Next steps + +- Review the [ingress-nginx provider](./providers/ingressnginx/) to understand the supported Ingress NGINX annotations. +- Review the [agentgateway emitter](./emitters/agentgateway/) to understand how providers such as ingress-nginx map to agentgateway-specific resources. +- Read the [emitter design](./emitters/) to learn more about emitters and providers. diff --git a/content/docs/kubernetes/1.1.x/migrate/emitters/_index.md b/content/docs/kubernetes/1.1.x/migrate/emitters/_index.md new file mode 100644 index 000000000..3b1e208d8 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/emitters/_index.md @@ -0,0 +1,67 @@ +--- +title: "Emitters" +weight: 20 +test: skip +--- + +This page is the [upstream design](https://github.com/kubernetes-sigs/ingress2gateway/blob/main/docs/emitters.md) for ingress2gateway’s **provider/emitter** architecture. + +> [!TIP] +> If you're here to understand **how ingress-nginx annotations are mapped to agentgateway**, jump directly to the [Agentgateway emitter](./agentgateway/). + +## Motivation + +At KubeCon NA 2025 in Atlanta, SIG Network announced the [retirement of Ingress NGINX](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/), giving users only four months to migrate to another solution. +Ingress NGINX has ~100 custom annotations that extend the Kubernetes Ingress resource that don't have a direct mapping to Gateway API. +Thus, for ingress2gateway to be useful at helping users migrate, the tool outputs implementation-specific resources to maximize coverage of said annotations. + +Third-party implementations are expected to write and maintain emitters for their specific Gateway API implementations. +This document outlines the design of the emitter system and the policies around third-party code contributions. + +## Architecture + +At a high level, ingress2gateway has two main components: providers and emitters. +Providers read Ingress-related resources and output an intermediate representation (IR) that is ingress-implementation-neutral. +Specifically, providers output `EmitterIR` (standard Gateway API resources and some IR that captures any additional information that cannot be expressed in Standard Gateway API). + +A common emitter reads the `EmitterIR` from the provider and translates it to potentially nonstandard Gateway API resources depending on configuration. +This provides a common component that implements logic such as "use any Gateway API feature vs only use stable Gateway API features". +The common emitter outputs `EmitterIR`. + +Implementation-specific emitters read the `EmitterIR` from the common emitter and output Gateway API resources along with implementation-specific resources. +Both providers and emitters MUST log any information that is lost in translation. +Ideally, when there is new IR and an emitter does not implement it, ingress2gateway should automatically emit notifications. + +Note that the emitters MAY modify the Gateway API resources as needed. +That said, emitters MUST NOT output non-Gateway API resources unless absolutely necessary. +For example, once Gateway API's CORS support is stable, ingress2gateway should no longer output [Envoy Gateway CORS](https://gateway.envoyproxy.io/docs/tasks/security/cors/). + +The architecture looks as follows. +The `provider` and `emitter` are implementation-specific components. +So the `provider` could be an "Ingress NGINX provider" that understands Ingress NGINX annotations, +and the `emitter` could be an "Envoy Gateway emitter" that outputs Envoy Gateway resources. + +```bash ++------------------------------+ +|(Ingress + related Resources) | ++-----+------------------------+ + | + | 1) provider.ToIR(resources) + v ++-----+--------+ +| Emitter IR 1 | ++-----+--------+ + | + | 2) CommonEmitter.Emit(IR 1) + v ++-----+--------+ +| Emitter IR 2 | ++-----+--------+ + | + | 3) emitter.Emit(IR 2) + v ++-----+-----------------------------+ +| Gateway API Resources | +| Implementation-specific Resources | ++-----------------------------------+ +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/emitters/agentgateway/_index.md b/content/docs/kubernetes/1.1.x/migrate/emitters/agentgateway/_index.md new file mode 100644 index 000000000..3549a7a91 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/emitters/agentgateway/_index.md @@ -0,0 +1,221 @@ +--- +title: "Agentgateway" +description: "How ingress-nginx annotations map to Gateway API + AgentgatewayPolicy resources" +weight: 30 +test: skip +--- + +The Agentgateway Emitter supports generating **Gateway API** resources plus **agentgateway**-specific extensions +from Ingress manifests using: + +- **Provider**: `ingress-nginx` + +**Note:** All other providers are ignored by the emitter. + +## What it outputs + +- Standard **Gateway API** objects (Gateways, HTTPRoutes, etc.) +- Agentgateway extension objects emitted as unstructured resources, e.g. **AgentgatewayPolicy**. + +The emitter also ensures that any generated Gateway resources use: + +- `spec.gatewayClassName: agentgateway` + +## Usage + +Ingress2gateway reads Ingress resources from a Kubernetes cluster or a file. It outputs the equivalent +Gateway API and agentgateway-specific resources in YAML or JSON format to stdout. The simplest case is to convert +all ingresses from the ingress-nginx provider: + +```shell +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway +``` + +This command: + +1. Reads the kubeconfig file to extract cluster credentials and the current active namespace. +2. Searches for ingress-nginx resources in that namespace. +3. Converts them to Gateway API resources (Gateways, HTTPRoutes) and AgentgatewayPolicy resources where applicable. + +## Options + +### `print` command + +| Flag | Default Value | Required | Description | +| -------------- | ----------------------- | -------- | ------------------------------------------------------------ | +| all-namespaces | False | No | If present, list the requested objects across all namespaces. Namespace in the current context is ignored even if specified with --namespace. | +| input-file | | No | Path to the manifest file. When set, the tool reads ingresses from the file instead of from the cluster. Supported files are yaml and json. | +| namespace | | No | If present, the namespace scope for the invocation. | +| output | yaml | No | The output format, either yaml or json. | +| providers | | Yes | Comma-separated list of providers (only ingress-nginx is supported). | +| emitter | standard | No | The emitter to use for generating Gateway API resources (supported values: standard, agentgateway). | +| kubeconfig | | No | The kubeconfig file to use when talking to the cluster. If the flag is not set, a set of standard locations can be searched for an existing kubeconfig file. | + +## Conversion of Ingress resources to Gateway API + +### Processing Order and Conflicts + +Ingress resources are processed in a defined order to ensure deterministic +Gateway API configuration. +This order also determines precedence of Ingress resources and routes in case +of conflicts. + +Ingress resources with the oldest creation timestamp are sorted first and therefore +given precedence. If creation timestamps are equal, sorting is done based +on the namespace/name of the resources. If an Ingress rule conflicts with another +(e.g. same path match but different backends), an error is reported for the +one that sorts later. + +Since the Ingress v1 spec does not define conflict resolution, this tool +adopts the following rules, which are similar to the [Gateway API conflict resolution +guidelines](https://gateway-api.sigs.k8s.io/guides/api-design/#conflicts). + +### Ingress resource fields to Gateway API fields + +Given a set of Ingress resources, `ingress2gateway` generates a Gateway with +various HTTP and HTTPS listeners as well as HTTPRoutes that represent equivalent +routing rules. + +| Ingress Field | Gateway API configuration | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ingressClassName` | If configured on an Ingress resource, the generated Gateway uses `spec.gatewayClassName: agentgateway`. | +| `defaultBackend` | If present, this configuration generates a Gateway Listener with no `hostname` specified as well as a catchall HTTPRoute that references this listener. The backend specified here is translated to a HTTPRoute `rules[].backendRefs[]` element. | +| `tls[].hosts` | Each host in an IngressTLS results in a HTTPS Listener on the generated Gateway with the following: `listeners[].hostname` = host as described, `listeners[].port` = `443`, `listeners[].protocol` = `HTTPS`, `listeners[].tls.mode` = `Terminate` | +| `tls[].secretName` | The secret specified here is referenced in the Gateway HTTPS Listeners mentioned above with the field `listeners[].tls.certificateRefs`. Each Listener for each host in an IngressTLS gets this secret. | +| `rules[].host` | If non-empty, each distinct value for this field in the provided Ingress resources results in a separate Gateway HTTP Listener with matching `listeners[].hostname`. `listeners[].port` is set to `80` and `listeners[].protocol` to `HTTP`. In addition, Ingress rules with the same hostname generate HTTPRoute rules in a HTTPRoute with `hostnames` containing it as the single element. If empty, similar to the `defaultBackend`, a Gateway Listener with no hostname configuration is generated (if one does not exist) and routing rules are generated in a catchall HTTPRoute. | +| `rules[].http.paths[].path` | This field translates to a HTTPRoute `rules[].matches[].path.value` configuration. | +| `rules[].http.paths[].pathType` | This field translates to a HTTPRoute `rules[].matches[].path.type` configuration. Ingress `Exact` = HTTPRoute `Exact` match. Ingress `Prefix` = HTTPRoute `PathPrefix` match. | +| `rules[].http.paths[].backend` | The backend specified here is translated to a HTTPRoute `rules[].backendRefs[]` element. | + +## Supported Annotations + +The agentgateway emitter projects many ingress-nginx annotations into **AgentgatewayPolicy** resources. Policies are either HTTPRoute-scoped (traffic behavior) or Service-scoped (backend behavior). + +### Traffic Behavior + +#### SSL Redirect + +- `nginx.ingress.kubernetes.io/ssl-redirect` +- `nginx.ingress.kubernetes.io/force-ssl-redirect` + +When either annotation is truthy, the emitter **splits** the generated HTTPRoute into two routes: + +- **HTTP redirect route** (bound to the Gateway HTTP listener): each rule includes a `RequestRedirect` filter with `scheme: https`, `statusCode: 301`, and no `backendRefs`. +- **HTTPS backend route** (bound to the Gateway HTTPS listener): preserves the original backendRefs; any existing `RequestRedirect` filters are removed. + +Naming: HTTP redirect route `-http-redirect`, HTTPS backend route `-https`. Redirect behavior uses only Gateway API objects (no agentgateway extensions). + +#### Rewrite Target + +- `nginx.ingress.kubernetes.io/rewrite-target` +- `nginx.ingress.kubernetes.io/use-regex: "true"` (for regex/capture-group behavior) + +Mapped into **AgentgatewayPolicy** using agentgateway’s `Traffic.Transformation` model by setting the HTTP pseudo-header `:path` in `spec.traffic.transformation.request.set`: + +- **Non-regex rewrite:** `rewrite-target: /new` β†’ `:path` set to a literal string value (e.g. a CEL string literal such as `"/new"`). +- **Regex rewrite:** `rewrite-target: /new/$1` with `use-regex: "true"` β†’ `:path` set using a CEL `regexReplace(...)` expression; the pattern is derived from the HTTPRoute rule path. + +Agentgateway uses CEL expressions, so literal strings appear as quoted CEL string literals in YAML. + +#### CORS + +- `nginx.ingress.kubernetes.io/enable-cors` +- `nginx.ingress.kubernetes.io/cors-allow-origin` +- `nginx.ingress.kubernetes.io/cors-allow-methods` +- `nginx.ingress.kubernetes.io/cors-allow-headers` +- `nginx.ingress.kubernetes.io/cors-expose-headers` +- `nginx.ingress.kubernetes.io/cors-allow-credentials` +- `nginx.ingress.kubernetes.io/cors-max-age` + +Mapped into **AgentgatewayPolicy** using agentgateway’s `Traffic.Cors` model (inlining the Gateway API `HTTPCORSFilter`). CORS is projected only when `enable-cors` is truthy **and** at least one value is present in `cors-allow-origin`. When CORS is projected, the emitter also adds a Gateway API `ResponseHeaderModifier` filter to the HTTPRoute rules to strip common CORS response headers from the upstream response so that the gateway policy controls effective CORS behavior. + +#### Basic Authentication + +- `nginx.ingress.kubernetes.io/auth-type` (supported: `basic`) +- `nginx.ingress.kubernetes.io/auth-secret` +- `nginx.ingress.kubernetes.io/auth-secret-type` (supported: `auth-file`) + +Mapped into **AgentgatewayPolicy** using agentgateway’s `Traffic.BasicAuthentication` model: `auth-secret` β†’ `AgentgatewayPolicy.spec.traffic.basicAuthentication.secretRef.name`. The emitter projects only the `secretRef` form. Agentgateway expects the Secret to contain a key named **`.htaccess`** with htpasswd-formatted content; ingress-nginx (auth-file) typically uses the key **`auth`**. To use the same Secret for both, create a dual-key Secret with both `auth` and `.htaccess` containing the same htpasswd content. The emitter may emit an **INFO** notification about this key difference. + +#### External Authentication + +- `nginx.ingress.kubernetes.io/auth-url` +- `nginx.ingress.kubernetes.io/auth-response-headers` + +Mapped into **AgentgatewayPolicy** using agentgateway’s `Traffic.ExtAuth` model: `auth-url` β†’ `AgentgatewayPolicy.spec.traffic.extAuth.backendRef` (parsed from URL host as a Kubernetes Service `*.svc[.cluster.local]`); path (when not `/`) β†’ `spec.traffic.extAuth.http.path` (CEL expression); `auth-response-headers` β†’ `spec.traffic.extAuth.http.allowedResponseHeaders[]`. Only in-cluster auth URLs that resolve to a Kubernetes Service are supported; the emitter uses the HTTP ext auth mode. + +#### Request Timeouts + +- `nginx.ingress.kubernetes.io/proxy-send-timeout` +- `nginx.ingress.kubernetes.io/proxy-read-timeout` + +Mapped into **AgentgatewayPolicy** using agentgateway’s `Traffic.Timeouts` model. If both annotations are set, the emitter uses the **larger** of the two values for `spec.traffic.timeouts.request`. + +#### Local Rate Limiting + +- `nginx.ingress.kubernetes.io/limit-rps` +- `nginx.ingress.kubernetes.io/limit-rpm` +- `nginx.ingress.kubernetes.io/limit-burst-multiplier` + +Mapped into **AgentgatewayPolicy** using agentgateway’s `LocalRateLimit` model: `limit-rps` β†’ requests per second; `limit-rpm` β†’ requests per minute; `limit-burst-multiplier` (when > 1) β†’ burst. + +### Backend Behavior + +#### Backend TLS + +- `nginx.ingress.kubernetes.io/proxy-ssl-secret` +- `nginx.ingress.kubernetes.io/proxy-ssl-server-name` +- `nginx.ingress.kubernetes.io/proxy-ssl-name` +- `nginx.ingress.kubernetes.io/proxy-ssl-verify` + +Mapped into **AgentgatewayPolicy** using agentgateway’s `BackendSimple.TLS` model: `proxy-ssl-name` β†’ `spec.backend.tls.sni`; `proxy-ssl-verify: "off"` β†’ `spec.backend.tls.insecureSkipVerify = All`; `proxy-ssl-secret` β†’ `spec.backend.tls.mtlsCertificateRef[0].name`. Backend TLS is emitted as **one AgentgatewayPolicy per referenced backend Service**. If `proxy-ssl-secret` is given as `namespace/name`, the emitter uses only `name` (Secret must be in the same namespace as the policy). + +#### Proxy Connect Timeout + +- `nginx.ingress.kubernetes.io/proxy-connect-timeout` + +Projected into a **Service-targeted** AgentgatewayPolicy: `AgentgatewayPolicy.spec.backend.tcp.connectTimeout`. The policy targets the covered Service backends via `spec.targetRefs`. If a route-level request timeout is also set, the emitter only projects `proxy-connect-timeout` when it is less than or equal to the projected request timeout. + +## AgentgatewayPolicy Projection + +Annotations for rate limit, timeouts, CORS, rewrite target, basic auth, and ext auth are converted into **AgentgatewayPolicy** resources. The emitter uses two shapes: + +- **HTTPRoute-scoped policies** for traffic-level behavior (rate limit, request timeouts, CORS, rewrite target, basic auth, ext auth). +- **Service-scoped policies** for backend behavior (backend TLS and proxy connect timeout). + +### Naming + +- Traffic policies: `metadata.name: `, `metadata.namespace: `. +- Backend TLS policies: `metadata.name: -backend-tls`, `metadata.namespace: `. +- Proxy connect timeout policies: `metadata.name: -backend-connect-timeout`, `metadata.namespace: `. + +### Attachment Semantics + +If a policy covers **all** backends of the generated HTTPRoute, it is attached using `spec.targetRefs` to the HTTPRoute. If a policy would cover only **some** (rule, backendRef) pairs, the emitter **returns an error** and does not emit agentgateway resources for that Ingress, because agentgateway does not support attaching AgentgatewayPolicy via per-backend HTTPRoute `ExtensionRef` filters. To avoid rejected or non-functional manifests, the emitter fails fast. Workarounds: split the source Ingress so each HTTPRoute can be fully covered by a policy, or adjust annotations so the policy applies uniformly. For backend TLS, the emitted Service-targeted policy applies cleanly without per-backend HTTPRoute filters. + +### Summary of policy types + +| Annotation category | Agentgateway resource | Scope | +|------------------------|-----------------------|--------------| +| Traffic behavior | AgentgatewayPolicy | HTTPRoute | +| Backend TLS / connect | AgentgatewayPolicy | Service | + +## Notifications + +Some conversions require follow-up user action that cannot be expressed safely as emitted manifests. The agentgateway emitter emits **INFO** notifications on the CLI in those cases. For example, when projecting **Basic Authentication**, the emitter might notify that ingress-nginx expects htpasswd under Secret key `auth` while agentgateway expects key `.htaccess`. + +## Limitations + +- Only the **ingress-nginx provider** is currently supported by the Agentgateway emitter. +- **Regex path matching** is not currently implemented for agentgateway output. +- Agentgateway does not support per-backend `HTTPRoute` `ExtensionRef` filters for policies; partial coverage leads to an error. + +## Supported but not translated + +The following annotations have equivalents in agentgateway but are not (as of yet) translated by this tool: + +- `nginx.ingress.kubernetes.io/auth-proxy-set-headers` β€” supported in AgentgatewayPolicy (e.g. `spec.traffic.extAuth.httpService.authorizationRequest.headersToAdd`). + +## Future work + +The code defines GVKs for additional agentgateway extension types (e.g. `AgentgatewayBackend`), but they are not yet emitted by the current implementation. diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/_index.md b/content/docs/kubernetes/1.1.x/migrate/examples/_index.md new file mode 100644 index 000000000..4454d71d8 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/_index.md @@ -0,0 +1,17 @@ +--- +title: "Examples" +weight: 40 +test: skip +--- + +This section contains step-by-step examples showing how to migrate common Ingress NGINX configurations to agentgateway using the `ingress2gateway` tool. + +Each example walks through the following steps: + +1. Creating a sample Ingress manifest. +2. Running `ingress2gateway` to convert it. +3. Reviewing the generated Gateway API and agentgateway resources. +4. Applying them to your cluster. + +Pick the example that matches your use case, or work through them all to get familiar with the migration workflow. + diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/backend-tls.md b/content/docs/kubernetes/1.1.x/migrate/examples/backend-tls.md new file mode 100644 index 000000000..6e7c55c2f --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/backend-tls.md @@ -0,0 +1,75 @@ +--- +title: "Backend TLS" +weight: 80 +--- + +When your backend requires TLS (re-encryption or mTLS), NGINX's `proxy-ssl-*` annotations are translated by the agentgateway emitter into an **AgentgatewayPolicy** with `spec.backend.tls` settings. The policy targets the backend Service. + +## Before: Ingress with backend TLS + +```bash +cat <<'EOF' > backend-tls-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: backend-tls-demo + annotations: + nginx.ingress.kubernetes.io/proxy-ssl-secret: "default/backend-tls-secret" + nginx.ingress.kubernetes.io/proxy-ssl-verify: "on" + nginx.ingress.kubernetes.io/proxy-ssl-name: "internal-api.example.com" +spec: + ingressClassName: agentgateway + rules: + - host: app.example.com + http: + paths: + - backend: + service: + name: secure-api + port: + number: 443 + path: /api + pathType: Prefix +EOF +``` + +## Convert + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway \ + --input-file backend-tls-ingress.yaml > backend-tls-agentgateway.yaml +``` + +## After: AgentgatewayPolicy with backend TLS + +```bash +cat backend-tls-agentgateway.yaml +``` + +The tool generates a Gateway, HTTPRoute(s), and an **AgentgatewayPolicy** per backend Service. The policy name is `-backend-tls`. With verification enabled (`proxy-ssl-verify: "on"`), the emitter configures mTLS via `mtlsCertificateRef` and does not set `insecureSkipVerify`: + +```yaml +apiVersion: agentgateway.dev/v1alpha1 +kind: AgentgatewayPolicy +metadata: + name: secure-api-backend-tls + namespace: default +spec: + targetRefs: + - group: "" + kind: Service + name: secure-api + backend: + tls: + mtlsCertificateRef: + - name: backend-tls-secret + sni: internal-api.example.com +``` + +If you set `proxy-ssl-verify: "off"`, the emitter would set `spec.backend.tls.insecureSkipVerify: All` instead of mTLS. + +## Apply + +```bash +kubectl apply -f backend-tls-agentgateway.yaml +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/basic.md b/content/docs/kubernetes/1.1.x/migrate/examples/basic.md new file mode 100644 index 000000000..2582826ae --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/basic.md @@ -0,0 +1,82 @@ +--- +title: "Basic Ingress" +weight: 10 +--- + +This example shows the simplest case: an Ingress with no special annotations. The tool converts it into a Gateway and HTTPRoute. + +## Before: Ingress + +```bash +cat <<'EOF' > basic-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: demo-localhost +spec: + ingressClassName: nginx + rules: + - host: demo.localdev.me + http: + paths: + - backend: + service: + name: echo-backend + port: + number: 8080 + path: / + pathType: Prefix +EOF +``` + +## Convert + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway --input-file basic-ingress.yaml > basic-agentgateway.yaml +``` + +## After: Gateway API resources + +```bash +cat basic-agentgateway.yaml +``` + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: nginx +spec: + gatewayClassName: agentgateway + listeners: + - hostname: demo.localdev.me + name: demo-localdev-me-http + port: 80 + protocol: HTTP +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: demo-localhost-demo-localdev-me +spec: + hostnames: + - demo.localdev.me + parentRefs: + - name: nginx + rules: + - backendRefs: + - name: echo-backend + port: 8080 + matches: + - path: + type: PathPrefix + value: / +``` + +## Apply and verify + +```bash +kubectl apply -f basic-agentgateway.yaml +kubectl get gateways +kubectl get httproutes +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/canary.md b/content/docs/kubernetes/1.1.x/migrate/examples/canary.md new file mode 100644 index 000000000..c0768a530 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/canary.md @@ -0,0 +1,98 @@ +--- +title: "Canary Deployments" +weight: 70 +--- + +NGINX's canary annotations let you split traffic between versions. The tool merges these into a single HTTPRoute with weighted backends. + +## Before: Primary and canary Ingresses + +```bash +cat <<'EOF' > canary-ingress.yaml +# Primary Ingress +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: app-primary +spec: + ingressClassName: nginx + rules: + - host: app.example.com + http: + paths: + - backend: + service: + name: app-v1 + port: + number: 80 + path: / + pathType: Prefix +--- +# Canary Ingress (20% of traffic) +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: app-canary + annotations: + nginx.ingress.kubernetes.io/canary: "true" + nginx.ingress.kubernetes.io/canary-weight: "20" +spec: + ingressClassName: nginx + rules: + - host: app.example.com + http: + paths: + - backend: + service: + name: app-v2 + port: + number: 80 + path: / + pathType: Prefix +EOF +``` + +## Convert + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway \ + --input-file canary-ingress.yaml > canary-agentgateway.yaml +``` + +## After: HTTPRoute with weighted backends + +```bash +cat canary-agentgateway.yaml +``` + +Both Ingresses merge into one HTTPRoute. Traffic is split 80/20: + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: app-primary-app-example-com +spec: + hostnames: + - app.example.com + parentRefs: + - name: nginx + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: app-v1 + port: 80 + weight: 80 + - name: app-v2 + port: 80 + weight: 20 +``` + +## Apply + +```bash +kubectl apply -f canary-agentgateway.yaml +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/cors.md b/content/docs/kubernetes/1.1.x/migrate/examples/cors.md new file mode 100644 index 000000000..930644cce --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/cors.md @@ -0,0 +1,85 @@ +--- +title: "CORS" +weight: 40 +--- + +CORS annotations on an Ingress are projected by the agentgateway emitter into an **AgentgatewayPolicy** with `spec.traffic.cors` settings. The policy targets the generated HTTPRoute. + +## Before: Ingress with CORS + +```bash +cat <<'EOF' > cors-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cors-demo + annotations: + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com, https://app.example.com" + nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization, Content-Type" + nginx.ingress.kubernetes.io/cors-max-age: "3600" +spec: + ingressClassName: agentgateway + rules: + - host: api.example.com + http: + paths: + - backend: + service: + name: api-service + port: + number: 8080 + path: / + pathType: Prefix +EOF +``` + +## Convert + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway \ + --input-file cors-ingress.yaml > cors-agentgateway.yaml +``` + +## After: AgentgatewayPolicy with CORS + +```bash +cat cors-agentgateway.yaml +``` + +The tool generates a Gateway, HTTPRoute(s), and an **AgentgatewayPolicy** named after the Ingress. CORS is under `spec.traffic.cors`: + +```yaml +apiVersion: agentgateway.dev/v1alpha1 +kind: AgentgatewayPolicy +metadata: + name: cors-demo + namespace: default +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: cors-demo-api-example-com + traffic: + cors: + allowOrigins: + - https://example.com + - https://app.example.com + allowMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + allowHeaders: + - Authorization + - Content-Type + maxAge: 3600 +``` + +## Apply + +```bash +kubectl apply -f cors-agentgateway.yaml +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/external-auth.md b/content/docs/kubernetes/1.1.x/migrate/examples/external-auth.md new file mode 100644 index 000000000..16f22e465 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/external-auth.md @@ -0,0 +1,80 @@ +--- +title: "External Auth" +weight: 60 +--- + +If you use NGINX's `auth-url` to call an in-cluster authentication service, the agentgateway emitter projects this into an **AgentgatewayPolicy** with `spec.traffic.extAuth`. Only in-cluster auth URLs that resolve to a Kubernetes Service (`*.svc`) are supported. + +## Before: Ingress with external auth + +```bash +cat <<'EOF' > external-auth-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ext-auth-demo + annotations: + nginx.ingress.kubernetes.io/auth-url: "http://auth-service.auth.svc.cluster.local/verify" + nginx.ingress.kubernetes.io/auth-response-headers: "X-User-ID, X-User-Email" +spec: + ingressClassName: agentgateway + rules: + - host: app.example.com + http: + paths: + - backend: + service: + name: protected-app + port: + number: 8080 + path: / + pathType: Prefix +EOF +``` + +## Convert + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway \ + --input-file external-auth-ingress.yaml > external-auth-agentgateway.yaml +``` + +## After: AgentgatewayPolicy with ext auth + +```bash +cat external-auth-agentgateway.yaml +``` + +The tool creates an **AgentgatewayPolicy** that configures the external auth service via `spec.traffic.extAuth`: + +```yaml +apiVersion: agentgateway.dev/v1alpha1 +kind: AgentgatewayPolicy +metadata: + name: ext-auth-demo + namespace: default +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: ext-auth-demo-app-example-com + traffic: + extAuth: + backendRef: + name: auth-service + namespace: auth + port: 80 + http: + path: '"/verify"' + allowedResponseHeaders: + - X-User-ID + - X-User-Email +``` + +The `path` value is a CEL string literal (the inner quotes are part of the expression). If the auth URL path is `/` or empty, the emitter omits `http.path`. + +## Apply + +```bash +kubectl apply -f external-auth-agentgateway.yaml +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/rate-limiting.md b/content/docs/kubernetes/1.1.x/migrate/examples/rate-limiting.md new file mode 100644 index 000000000..10b2e30e3 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/rate-limiting.md @@ -0,0 +1,73 @@ +--- +title: "Rate Limiting" +weight: 30 +--- + +NGINX's `limit-rps`, `limit-rpm`, and `limit-burst-multiplier` annotations are projected by the agentgateway emitter into an **AgentgatewayPolicy** with `spec.traffic.rateLimit.local` (requests, unit, and optional burst). + +## Before: Ingress with rate limits + +```bash +cat <<'EOF' > ratelimit-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ratelimit-demo + annotations: + nginx.ingress.kubernetes.io/limit-rps: "10" + nginx.ingress.kubernetes.io/limit-burst-multiplier: "2" +spec: + ingressClassName: agentgateway + rules: + - host: api.example.com + http: + paths: + - backend: + service: + name: api-service + port: + number: 8080 + path: / + pathType: Prefix +EOF +``` + +## Convert + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway \ + --input-file ratelimit-ingress.yaml > ratelimit-agentgateway.yaml +``` + +## After: AgentgatewayPolicy with local rate limit + +```bash +cat ratelimit-agentgateway.yaml +``` + +The generated **AgentgatewayPolicy** uses agentgateway's `LocalRateLimit` model. With 10 RPS and a 2x burst multiplier, you get `requests: 10`, `unit: Seconds`, and `burst: 20`: + +```yaml +apiVersion: agentgateway.dev/v1alpha1 +kind: AgentgatewayPolicy +metadata: + name: ratelimit-demo + namespace: default +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: ratelimit-demo-api-example-com + traffic: + rateLimit: + local: + - requests: 10 + unit: Seconds + burst: 20 +``` + +## Apply + +```bash +kubectl apply -f ratelimit-agentgateway.yaml +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/examples/ssl-redirect.md b/content/docs/kubernetes/1.1.x/migrate/examples/ssl-redirect.md new file mode 100644 index 000000000..44609a4b8 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/examples/ssl-redirect.md @@ -0,0 +1,79 @@ +--- +title: "SSL Redirect" +weight: 50 +--- + +The `ssl-redirect` annotation tells NGINX to redirect HTTP to HTTPS. In Gateway API, this becomes a `RequestRedirect` filter on the HTTPRoute. + +## Before: Ingress with SSL redirect + +```bash +cat <<'EOF' > ssl-redirect-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ssl-redirect-demo + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + ingressClassName: nginx + rules: + - host: secure.example.com + http: + paths: + - backend: + service: + name: web-app + port: + number: 8080 + path: / + pathType: Prefix +EOF +``` + +## Convert + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway \ + --input-file ssl-redirect-ingress.yaml > ssl-redirect-agentgateway.yaml +``` + +## After: HTTPRoute(s) with redirect + +```bash +cat ssl-redirect-agentgateway.yaml +``` + +With the agentgateway emitter, SSL redirect is implemented by **splitting** the route: an HTTP route includes a `RequestRedirect` filter (301 to HTTPS) with no backends, and a separate HTTPS route carries the backend traffic. Shown below is the HTTP redirect route: + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: ssl-redirect-demo-secure-example-com +spec: + hostnames: + - secure.example.com + parentRefs: + - name: nginx + rules: + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 +``` + +{{% callout note %}} +NGINX uses status code 308 by default, but Gateway API standardizes on 301. +{{% /callout %}} + +## Apply + +```bash +kubectl apply -f ssl-redirect-agentgateway.yaml +``` diff --git a/content/docs/kubernetes/1.1.x/migrate/install/_index.md b/content/docs/kubernetes/1.1.x/migrate/install/_index.md new file mode 100644 index 000000000..377038057 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/install/_index.md @@ -0,0 +1,11 @@ +--- +title: "Install" +weight: 10 +test: skip +--- + +Select the operating system for installing ingress2gateway. + +- [macOS](./macos/) +- [Linux](./linux/) +- [Windows](./windows/) \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/migrate/install/linux/_index.md b/content/docs/kubernetes/1.1.x/migrate/install/linux/_index.md new file mode 100644 index 000000000..bec00a7e6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/install/linux/_index.md @@ -0,0 +1,72 @@ +--- +title: "Linux installation" +weight: 20 +--- + +## Install ingress2gateway on Linux + +1. Set your environment variables. + + ```bash + VERSION=v0.4.0 + OS=Linux + # One of arm64|x86_64|i386 + ARCH=arm64 + ``` + + Refer to the [releases page](https://github.com/kgateway-dev/ingress2gateway/releases) for a list of published ingress2gateway versions. + +2. Download the release. + + ```bash + curl -LO "https://github.com/kgateway-dev/ingress2gateway/releases/download/${VERSION}/ingress2gateway_${OS}_${ARCH}.tar.gz" + ``` + +3. Optional: Validate the release tarball. + + Download the ingress2gateway checksum file. + + ```bash + curl -LO https://github.com/kgateway-dev/ingress2gateway/releases/download/${VERSION}/checksums.txt + ``` + + Validate the binary against the checksum file. + + ```bash + grep -F " ingress2gateway_${OS}_${ARCH}.tar.gz" checksums.txt | shasum -a 256 --check + ``` + + If valid, the output is: + + ```bash + ingress2gateway_$OS_$ARCH.tar.gz: OK + ``` + +4. Extract the tarball. + + ```bash + tar -xvf ingress2gateway_${OS}_${ARCH}.tar.gz + ``` + +5. Install ingress2gateway. + + ```bash + sudo install -o root -g root -m 0755 ingress2gateway /usr/local/bin/ingress2gateway + ``` + + **Note:** If you do not have root access on the target system, you can still install ingress2gateway to the ~/.local/bin directory: + + ```bash + chmod +x ingress2gateway + mkdir -p ~/.local/bin + mv ./ingress2gateway ~/.local/bin/ingress2gateway + # and then append (or prepend) ~/.local/bin to $PATH + ``` + +6. Test the installation by checking the version of the binary. + + ```bash + ingress2gateway version + ``` + + **Note:** Make sure /usr/local/bin is in your PATH environment variable. diff --git a/content/docs/kubernetes/1.1.x/migrate/install/macos/_index.md b/content/docs/kubernetes/1.1.x/migrate/install/macos/_index.md new file mode 100644 index 000000000..9af360e06 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/install/macos/_index.md @@ -0,0 +1,64 @@ +--- +title: "macOS installation" +weight: 10 +--- + +## Install ingress2gateway on macOS + +1. Set the environment variables. + + ```bash + VERSION=v0.4.0 + OS=Darwin + # One of arm64|x86_64 + ARCH=arm64 + ``` + + Refer to the [releases page](https://github.com/kgateway-dev/ingress2gateway/releases) for a list of published ingress2gateway versions. + +2. Download the release. + + ```bash + curl -LO "https://github.com/kgateway-dev/ingress2gateway/releases/download/${VERSION}/ingress2gateway_${OS}_${ARCH}.tar.gz" + ``` + +3. Optional: Validate the release tarball. + + Download the ingress2gateway checksum file. + + ```bash + curl -LO https://github.com/kgateway-dev/ingress2gateway/releases/download/${VERSION}/checksums.txt + ``` + + Validate the binary against the checksum file. + + ```bash + grep -F " ingress2gateway_${OS}_${ARCH}.tar.gz" checksums.txt | shasum -a 256 --check + ``` + + If valid, the output is: + + ```bash + ingress2gateway_$OS_$ARCH.tar.gz: OK + ``` + +4. Extract the tarball. + + ```bash + tar -xvf ingress2gateway_${OS}_${ARCH}.tar.gz + ``` + +5. Move the binary to a file location on your system PATH. + + ```bash + sudo mv ./ingress2gateway /usr/local/bin/ingress2gateway + sudo chown root: /usr/local/bin/ingress2gateway + ``` + + **Note:** Make sure /usr/local/bin is in your PATH environment variable. + +6. Test the installation by checking the version of the binary. + + ```bash + ingress2gateway version + ``` diff --git a/content/docs/kubernetes/1.1.x/migrate/install/windows/_index.md b/content/docs/kubernetes/1.1.x/migrate/install/windows/_index.md new file mode 100644 index 000000000..d12e6f744 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/install/windows/_index.md @@ -0,0 +1,59 @@ +--- +title: "Windows installation" +weight: 30 +--- + +## Install ingress2gateway on Windows + +1. Set your environment variables. + + ```bash + VERSION=v0.4.0 + OS=Windows + # One of arm64|x86_64|i386 + ARCH=arm64 + ``` + + Refer to the [releases page](https://github.com/kgateway-dev/ingress2gateway/releases) for a list of published ingress2gateway versions. + +2. Download the release. + + ```bash + curl -LO "https://github.com/kgateway-dev/ingress2gateway/releases/download/${VERSION}/ingress2gateway_${OS}_${ARCH}.zip" + ``` + +3. Optional: Validate the release tarball. + + Download the ingress2gateway checksum file. + + ```bash + curl -LO https://github.com/kgateway-dev/ingress2gateway/releases/download/${VERSION}/checksums.txt + ``` + + Validate the binary against the checksum file. + + Using Command Prompt, manually compare CertUtil's output to the downloaded checksum file. + + ```bash + CertUtil -hashfile ingress2gateway_${OS}_${ARCH}.zip SHA256 + ``` + + If valid, the output is: + + ```bash + ingress2gateway_$OS_$ARCH.zip: OK + ``` + +4. Extract the tarball. + + ```bash + tar -xvf ingress2gateway_${OS}_${ARCH}.zip + ``` + +5. Append or prepend the binary to your PATH environment variable. + +6. Test the installation by checking the version of the binary. + + ```bash + ingress2gateway version + ``` diff --git a/content/docs/kubernetes/1.1.x/migrate/providers/_index.md b/content/docs/kubernetes/1.1.x/migrate/providers/_index.md new file mode 100644 index 000000000..6ae65d47b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/providers/_index.md @@ -0,0 +1,11 @@ +--- +title: "Providers" +weight: 20 +test: skip +--- + +Providers provide the source input for migrating to **Gateway API** and **agentgateway** resources. + +Select the provider to use with ingress2gateway. + +- [ingress-nginx](./ingressnginx/) diff --git a/content/docs/kubernetes/1.1.x/migrate/providers/ingressnginx/_index.md b/content/docs/kubernetes/1.1.x/migrate/providers/ingressnginx/_index.md new file mode 100644 index 000000000..79d3c754a --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/providers/ingressnginx/_index.md @@ -0,0 +1,167 @@ +--- +title: "Ingress NGINX" +description: "How ingress-nginx annotations map to Gateway API + agentgateway resources" +weight: 30 +test: skip +--- + +The `ingress-nginx` provider defines the source resources to be translated, e.g. an Ingress resource with Ingress NGINX-specific annotations. + +**Note:** Some annotations may be translated into agentgateway resources or user-facing notifications. + +## Supported Annotations + +The `ingress-nginx` provider currently supports an Ingress with the following annotations. + +### Canary / Traffic Shaping + +- `nginx.ingress.kubernetes.io/canary`: If set to `true`, enables weighted backends. + +- `nginx.ingress.kubernetes.io/canary-by-header`: Specifies the header name used to generate an HTTPHeaderMatch. + +- `nginx.ingress.kubernetes.io/canary-by-header-value`: Specifies the exact header value to match. + +- `nginx.ingress.kubernetes.io/canary-by-header-pattern`: Specifies a regex pattern used in the header match. + +- `nginx.ingress.kubernetes.io/canary-weight`: Specifies the backend weight for traffic shifting. + +- `nginx.ingress.kubernetes.io/canary-weight-total`: Defines the total weight used when calculating backend percentages. + +--- + +### Request / Body Size + +- `nginx.ingress.kubernetes.io/client-body-buffer-size`: Sets the maximum request body size when `proxy-body-size` is not present. **Note:** The agentgateway emitter does not currently project body/buffer size to AgentgatewayPolicy. + +- **Note (regex-mode constraint):** Ingress NGINX session cookie paths do not support regex. If regex-mode is enabled for a host (via `use-regex: "true"` or + `rewrite-target`) and cookie affinity is used, `session-cookie-path` must be set; the provider validates this and emits an error if it is missing. + +- `nginx.ingress.kubernetes.io/proxy-body-size`: Sets the maximum allowed request body size. Takes precedence over `client-body-buffer-size`. **Note:** The agentgateway emitter does not currently project body/buffer size to AgentgatewayPolicy. + +--- + +### CORS + +- `nginx.ingress.kubernetes.io/enable-cors`: Enables CORS policy generation. When set to "true", enables CORS handling for the Ingress. + For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.cors`. +- `nginx.ingress.kubernetes.io/cors-allow-origin`: Comma-separated list of origins (e.g. "https://example.com, https://another.com"). + For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.cors.allowOrigins`. +- `nginx.ingress.kubernetes.io/cors-allow-credentials`: Controls whether credentials are allowed in cross-origin requests ("true" / "false"). + For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.cors.allowCredentials`. +- `nginx.ingress.kubernetes.io/cors-allow-headers`: A comma-separated list of allowed request headers. For agentgateway, + this maps to `AgentgatewayPolicy.spec.traffic.cors.allowHeaders`. +- `nginx.ingress.kubernetes.io/cors-expose-headers`: A comma-separated list of HTTP response headers that can be exposed to client-side + scripts in response to a cross-origin request. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.cors.exposeHeaders`. +- `nginx.ingress.kubernetes.io/cors-allow-methods`: A comma-separated list of allowed HTTP methods (e.g. "GET, POST, OPTIONS"). + For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.cors.allowMethods`. +- `nginx.ingress.kubernetes.io/cors-max-age`: Controls how long preflight responses may be cached (in seconds). For the agentgateway + implementation, this maps to `AgentgatewayPolicy.spec.traffic.cors.maxAge`. + +### Rate Limiting + +- `nginx.ingress.kubernetes.io/limit-rps`: Requests per second limit. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.rateLimit.local` (requests, unit: Seconds). + +- `nginx.ingress.kubernetes.io/limit-rpm`: Requests per minute limit. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.rateLimit.local` (requests, unit: Minutes). + +- `nginx.ingress.kubernetes.io/limit-burst-multiplier`: Burst multiplier for rate limiting. For agentgateway, this maps to `burst` in the same `rateLimit.local` entry. + +--- + +### Timeouts + +- `nginx.ingress.kubernetes.io/proxy-send-timeout`: Controls the request timeout. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.timeouts.request`. + +- `nginx.ingress.kubernetes.io/proxy-read-timeout`: Controls stream idle timeout. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.timeouts.request`. If both are set, the emitter uses the larger value. + +--- + +### External Auth + +- `nginx.ingress.kubernetes.io/auth-url`: Specifies the URL of an external authentication service. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.extAuth` (backendRef from URL host; only in-cluster `*.svc` URLs are supported). +- `nginx.ingress.kubernetes.io/auth-response-headers`: Comma-separated list of headers to pass to backend once authentication request completes. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.extAuth.http.allowedResponseHeaders`. + +### Basic Auth + +- `nginx.ingress.kubernetes.io/auth-type`: Must be set to `"basic"` to enable basic authentication. For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.basicAuthentication`. +- `nginx.ingress.kubernetes.io/auth-secret`: Specifies the secret containing basic auth credentials in `namespace/name` format (or just `name` if in the same namespace). For agentgateway, this maps to `AgentgatewayPolicy.spec.traffic.basicAuthentication.secretRef.name`. Agentgateway expects the Secret key **`.htaccess`** (ingress-nginx often uses `auth`); the emitter may emit an INFO notification about this. +- `nginx.ingress.kubernetes.io/auth-secret-type`: **Only `"auth-file"` is supported** (default). The secret must contain an htpasswd file in the key `"auth"`. Only SHA hashed passwords are supported. For agentgateway, the emitter projects only the secretRef form; agentgateway expects key `.htaccess`. + +--- + +### Backend Protocol + +- `nginx.ingress.kubernetes.io/backend-protocol`: Indicates the L7 protocol that is used to communicate with the proxied backend. + - **Supported values (recorded):** `GRPC`, `GRPCS` β€” the provider records protocol intent; the agentgateway emitter does **not** currently emit AgentgatewayBackend or Service resources for this. It may emit an **INFO** notification with a `kubectl patch` command to set `spec.ports[].appProtocol` on the existing Service. + - **Values treated as default HTTP/1.x (no-op):** `HTTP`, `HTTPS`, `AUTO_HTTP` + - **Unsupported values (rejected):** `FCGI` (and others) + +--- + +### Backend (Upstream) Configuration + +- `nginx.ingress.kubernetes.io/proxy-connect-timeout`: Controls the upstream connection timeout. For agentgateway, + this maps to `AgentgatewayPolicy.spec.backend.tcp.connectTimeout` (per-Service policy targeting the backend Service). +- `nginx.ingress.kubernetes.io/load-balance`: Sets the algorithm to use for load balancing. The only supported value is `round_robin`. **Note:** The agentgateway emitter does not currently project this to AgentgatewayPolicy. + +**Note:** For agentgateway, if multiple Ingress resources reference the same Service with different `proxy-connect-timeout` values, ingress2gateway emits warnings; the emitter produces one AgentgatewayPolicy per Service and conflicting values may result in warnings. + +--- + +### Backend TLS + +- `nginx.ingress.kubernetes.io/proxy-ssl-secret`: Specifies a Secret containing client certificate (`tls.crt`), client key (`tls.key`), and optionally CA certificate (`ca.crt`) in PEM format. The secret name can be specified as `secretName` (same namespace) or `namespace/secretName`. For agentgateway, this maps to `AgentgatewayPolicy.spec.backend.tls.mtlsCertificateRef[0].name` (emitter uses only the name; Secret must be in the same namespace as the policy). + +- `nginx.ingress.kubernetes.io/proxy-ssl-verify`: Enables or disables verification of the proxied HTTPS server certificate. Values: `"on"` or `"off"` (default: `"off"`). For agentgateway, `"off"` maps to `AgentgatewayPolicy.spec.backend.tls.insecureSkipVerify: All`; when verification is on, the emitter does not set `insecureSkipVerify` and configures mTLS via `mtlsCertificateRef` when a secret is provided. + +- `nginx.ingress.kubernetes.io/proxy-ssl-name`: Overrides the server name used to verify the certificate of the proxied HTTPS server. This value is also passed through SNI. For agentgateway, this maps to `AgentgatewayPolicy.spec.backend.tls.sni`. + +- `nginx.ingress.kubernetes.io/proxy-ssl-server-name`: When set to `"on"`, enables SNI; the SNI value still comes from `proxy-ssl-name`. For agentgateway, the emitter projects SNI via `proxy-ssl-name`. + +**Note:** For agentgateway, backend TLS is emitted as one **AgentgatewayPolicy per backend Service** (name `-backend-tls`), targeting the Service via `spec.targetRefs`. Conflicting settings for the same Service may result in warnings. + +--- + +### Session Affinity + +- `nginx.ingress.kubernetes.io/affinity`: Enables and sets the affinity type in all Upstreams of an Ingress. The only affinity type available for NGINX is "cookie". **Note:** The **agentgateway emitter does not support session affinity.** It does not emit AgentgatewayPolicy or equivalent for cookie affinity. Other emitters (e.g. kgateway) may map this to BackendConfigPolicy/ringHash. + +- `nginx.ingress.kubernetes.io/session-cookie-name`, `session-cookie-path`, `session-cookie-domain`, `session-cookie-samesite`, `session-cookie-expires`, `session-cookie-max-age`, `session-cookie-secure`: Define cookie attributes for session affinity. **Not projected by the agentgateway emitter.** + +--- + +### SSL Redirect + +- `nginx.ingress.kubernetes.io/ssl-redirect`: When set to `"true"`, enables SSL redirect for HTTP requests. For agentgateway, the emitter **splits** the HTTPRoute into an HTTP redirect route (RequestRedirect filter, 301 to HTTPS, no backends) and a separate HTTPS backend route. Note that ingress-nginx redirects with code 308; Gateway API uses 301. + +- `nginx.ingress.kubernetes.io/force-ssl-redirect`: When set to `"true"`, enables SSL redirect. Treated identically to `ssl-redirect` by the agentgateway emitter. + +**Note:** Both annotations are supported and treated identically. With the agentgateway emitter, SSL redirect is implemented using Gateway API only (no AgentgatewayPolicy). + +--- + +### Regex Path Matching and Rewrites + +- `nginx.ingress.kubernetes.io/use-regex`: When set to `"true"`, indicates that the paths defined on that Ingress should be treated as regular expressions. + Uses host-group semantics: if any Ingress contributing rules for a given host has `use-regex: "true"`, regex-style path matching is enforced on **all** + paths for that host (across all contributing Ingresses). + +- `nginx.ingress.kubernetes.io/rewrite-target`: Rewrites the request path using regex rewrite semantics. + Uses host-group semantics: if any Ingress contributing rules for a given host sets `rewrite-target`, regex-style path matching is enforced on **all** + paths for that host (across all contributing Ingresses), consistent with ingress-nginx behavior. + +For agentgateway: + +- **Regex path matching** is **not currently implemented** for agentgateway output (emitter limitation). +- `rewrite-target` is projected into `AgentgatewayPolicy.spec.traffic.transformation.request.set` (HTTP pseudo-header `:path`), using CEL for literal or regexReplace-style rewrites. The policy is attached via `targetRefs` to the HTTPRoute (full coverage only; partial coverage causes an error). + +--- + +## Provider Limitations + +- For this documentation, the **agentgateway emitter** is the supported target; session affinity and regex path matching are not supported by the agentgateway emitter. +- Some NGINX behaviors cannot be reproduced exactly due to differences between NGINX and semantics of other proxy implementations. +- Regex-mode is implemented by converting HTTPRoute path matches to `RegularExpression`. Some ingress-nginx details (such as case-insensitive `~*` behavior) + might not be reproduced exactly depending on the underlying Gateway API / Envoy behavior and the patterns provided. + +If you rely on annotations not listed above, please open an [issue](https://github.com/kgateway-dev/ingress2gateway/issues) or be prepared to apply +post-migration manual adjustments. diff --git a/content/docs/kubernetes/1.1.x/migrate/reference/_index.md b/content/docs/kubernetes/1.1.x/migrate/reference/_index.md new file mode 100644 index 000000000..f7e5154a9 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/reference/_index.md @@ -0,0 +1,7 @@ +--- +title: "Reference" +weight: 50 +test: skip +--- + +- [CLI](./cli/) diff --git a/content/docs/kubernetes/1.1.x/migrate/reference/cli/_index.md b/content/docs/kubernetes/1.1.x/migrate/reference/cli/_index.md new file mode 100644 index 000000000..fad849332 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/migrate/reference/cli/_index.md @@ -0,0 +1,33 @@ +--- +title: "CLI" +weight: 10 +test: skip +--- +This page focuses on the commands you’ll use most often. + +## `print` + +Translates input manifests and prints generated resources. + +Typical usage: + +```bash +ingress2gateway print --providers=ingress-nginx --emitter=agentgateway --input-file ./ingress.yaml +``` + +## `version` + +Prints version information. + +```bash +ingress2gateway version +``` + +## Help + +For the complete flag reference for your build: + +```bash +ingress2gateway --help +ingress2gateway print --help +``` diff --git a/content/docs/kubernetes/1.1.x/not-in-version.md b/content/docs/kubernetes/1.1.x/not-in-version.md new file mode 100644 index 000000000..2f2a9ef89 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/not-in-version.md @@ -0,0 +1,9 @@ +--- +title: Topic not available in this version +description: This topic isn't available in this version of the documentation. +test: skip +build: + list: never +--- + +{{< reuse "agw-docs/pages/not-in-version.md" >}} diff --git a/content/docs/kubernetes/1.1.x/observability/_index.md b/content/docs/kubernetes/1.1.x/observability/_index.md new file mode 100644 index 000000000..eb3f06efb --- /dev/null +++ b/content/docs/kubernetes/1.1.x/observability/_index.md @@ -0,0 +1,16 @@ +--- +title: Observability +weight: 700 +icon: monitoring +description: Tracing, prompt logging, cost tracking, and metrics for your gateways. +next: /operations +prev: /integrations +test: skip +--- + +Gain insight into the health and performance of your gateway environment. Get prompt logging, cost tracking, and an audit trail for LLM traffic via OpenTelemetry. + + +## External observability platforms + +For specialized LLM observability platforms with prompt logging, cost tracking, and evaluation capabilities, see the [LLM Observability integrations]({{< link-hextra path="/integrations/llm-observability/" >}}). diff --git a/content/docs/kubernetes/1.1.x/observability/agentgateway.json b/content/docs/kubernetes/1.1.x/observability/agentgateway.json new file mode 100644 index 000000000..442651153 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/observability/agentgateway.json @@ -0,0 +1,776 @@ +{ + "annotations": {}, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 0, + "panels": [], + "title": "Overview", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 1 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum(label_replace(sum by (pod,namespace) (container_memory_working_set_bytes{image=\"\",namespace=~\"$namespace\"}), \"nsnameexists\", \"yes\", \"namespace\", \".*\") / on(pod, namespace, nsnameexists) label_replace(label_join(agentgateway_build_info, \"nsname\", \"/\", \"namespace\", \"gateway_networking_k8s_io_gateway_name\"), \"nsnameexists\", \"yes\", \"nsname\", \"$gateway\")) without (nsnameexists)", + "legendFormat": "{{namespace}}/{{pod}}", + "refId": "" + } + ], + "title": "Memory", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 1 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (pod,namespace) (sum(label_replace(irate(container_cpu_usage_seconds_total{image=\"\",namespace=~\"$namespace\"}[$__rate_interval]), \"nsnameexists\", \"yes\", \"namespace\", \".*\") / on(pod, namespace, nsnameexists) label_replace(label_join(agentgateway_build_info, \"nsname\", \"/\", \"namespace\", \"gateway_networking_k8s_io_gateway_name\"), \"nsnameexists\", \"yes\", \"nsname\", \"$gateway\")) without (nsnameexists))", + "legendFormat": "{{namespace}}/{{pod}}", + "refId": "" + } + ], + "title": "CPU", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 0, + "panels": [], + "title": "Requests", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 12 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (pod,namespace) (rate(agentgateway_requests_total{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval]))", + "legendFormat": "{{namespace}}/{{pod}}", + "refId": "" + } + ], + "title": "Requests (by Pod)", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 12 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (gateway) (rate(agentgateway_requests_total{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval]))", + "legendFormat": "{{gateway}}", + "refId": "" + } + ], + "title": "Requests (by Gateway)", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 22 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (gateway,status) (rate(agentgateway_requests_total{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval]))", + "legendFormat": "{{gateway}}: {{status}}", + "refId": "" + } + ], + "title": "Requests (by Status)", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 22 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (gateway,reason) (rate(agentgateway_requests_total{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval]))", + "legendFormat": "{{gateway}}: {{reason}}", + "refId": "" + } + ], + "title": "Requests (by Reason)", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 32 + }, + "id": 0, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 33 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (gen_ai_token_type,gen_ai_request_model,gateway) (rate(agentgateway_gen_ai_client_token_usage_sum{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval]))", + "legendFormat": "{{gateway}}: {{gen_ai_request_model}} ({{gen_ai_token_type}})", + "refId": "" + } + ], + "title": "Token Consumption", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 33 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "histogram_quantile(0.5, sum by (le,gateway,gen_ai_request_model) (rate(agentgateway_gen_ai_server_time_to_first_token_bucket{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval])))", + "legendFormat": "{{gateway}}: {{gen_ai_request_model}}", + "refId": "" + } + ], + "title": "Time To First Token", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 43 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "histogram_quantile(0.5, sum by (le,gateway,gen_ai_request_model) (rate(agentgateway_gen_ai_server_request_duration_bucket{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval])))", + "legendFormat": "{{gateway}}: {{gen_ai_request_model}}", + "refId": "" + } + ], + "title": "Request Time", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "tps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 43 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "1 / histogram_quantile(0.5, sum by (le,gateway,gen_ai_request_model) (rate(agentgateway_gen_ai_server_time_per_output_token_bucket{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval])))", + "legendFormat": "{{gateway}}: {{gen_ai_request_model}}", + "refId": "" + } + ], + "title": "Tokens Per Second", + "transformations": [], + "transparent": false, + "type": "timeseries" + } + ], + "title": "LLM", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 53 + }, + "id": 0, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 54 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (gateway,method) (rate(agentgateway_mcp_requests_total{namespace=~\"$namespace\",gateway=~\"$gateway\"}[$__rate_interval]))", + "legendFormat": "{{gateway}}: {{method}}", + "refId": "" + } + ], + "title": "MCP Calls (by method)", + "transformations": [], + "transparent": false, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "fillOpacity": 10, + "gradientMode": "opacity", + "showPoints": "never" + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 54 + }, + "interval": "5s", + "options": { + "legend": { + "calcs": [ + "last", + "max", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "asc" + } + }, + "repeatDirection": "h", + "targets": [ + { + "expr": "sum by (gateway,server,resource) (rate(agentgateway_mcp_requests_total{namespace=~\"$namespace\",gateway=~\"$gateway\",method=\"tools/call\"}[$__rate_interval]))", + "legendFormat": "{{gateway}}: {{server}}/{{resource}}", + "refId": "" + } + ], + "title": "Tool Calls (by tool)", + "transformations": [], + "transparent": false, + "type": "timeseries" + } + ], + "title": "MCP", + "type": "row" + } + ], + "refresh": "15s", + "schemaVersion": 36, + "style": "dark", + "templating": { + "list": [ + { + "auto": false, + "auto_count": 30, + "auto_min": "10s", + "hide": 0, + "id": "00000000-0000-0000-0000-000000000000", + "includeAll": false, + "multi": false, + "name": "datasource", + "query": "prometheus", + "skipUrlSync": false, + "type": "datasource" + }, + { + "auto": false, + "auto_count": 30, + "auto_min": "10s", + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "hide": 0, + "id": "00000000-0000-0000-0000-000000000000", + "includeAll": true, + "label": "Namespace", + "multi": true, + "name": "namespace", + "query": "label_values(agentgateway_build_info,namespace)", + "refresh": 2, + "skipUrlSync": false, + "sort": 1, + "type": "query" + }, + { + "auto": false, + "auto_count": 30, + "auto_min": "10s", + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, + "hide": 0, + "id": "00000000-0000-0000-0000-000000000000", + "includeAll": true, + "label": "Gateway", + "multi": true, + "name": "gateway", + "query": "query_result(label_join(agentgateway_build_info{namespace=~\"$namespace\"}, \"text\", \"/\", \"namespace\", \"gateway_networking_k8s_io_gateway_name\"))", + "refresh": 2, + "regex": "/.*text=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timezone": "browser", + "title": "Agentgateway", + "uid": "agentgateway" + } \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/observability/control-plane-metrics.md b/content/docs/kubernetes/1.1.x/observability/control-plane-metrics.md new file mode 100644 index 000000000..bef63e704 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/observability/control-plane-metrics.md @@ -0,0 +1,12 @@ +--- +title: Control plane metrics +weight: 20 +test: + control-plane-metrics: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/observability/control-plane-metrics.md + path: control-plane-metrics +--- + +{{< reuse "agw-docs/pages/observability/control-plane-metrics.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/observability/nacks.md b/content/docs/kubernetes/1.1.x/observability/nacks.md new file mode 100644 index 000000000..de414345e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/observability/nacks.md @@ -0,0 +1,14 @@ +--- +title: Monitoring agentgateway proxies for NACKs +weight: 80 +test: + nacks: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/observability/nacks.md + path: nacks +--- + +{{< reuse "agw-docs/pages/agentgateway/monitoring-agentgateway-nacks.md" >}} diff --git a/content/docs/kubernetes/1.1.x/observability/otel-stack.md b/content/docs/kubernetes/1.1.x/observability/otel-stack.md new file mode 100644 index 000000000..1a0708eea --- /dev/null +++ b/content/docs/kubernetes/1.1.x/observability/otel-stack.md @@ -0,0 +1,52 @@ +--- +title: OTel stack +weight: 10 +test: + otel-stack: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/observability/otel-stack.md + path: otel-stack +--- + +{{< reuse "agw-docs/snippets/agentgateway/otel-prereq.md" >}} + +{{< reuse "agw-docs/pages/observability/otel-stack.md" >}} + +## Step 4: Explore Grafana dashboards + +{{< reuse "agw-docs/snippets/agentgateway/grafana-dashboards.md" >}} + + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +1. Remove the configmap for the Envoy gateway proxy dashboard and delete the `envoy.json` file. + ```sh + kubectl delete cm {{< reuse "agw-docs/snippets/pod-name.md" >}}-dashboard -n telemetry + rm {{< reuse "agw-docs/snippets/pod-name.md" >}}.json + ``` + +2. Uninstall the Grafana Loki and Tempo components. + ```sh + helm uninstall loki -n telemetry + helm uninstall tempo -n telemetry + ``` + +3. Uninstall the OpenTelemetry collectors. + ```sh + helm uninstall opentelemetry-collector-metrics -n telemetry + helm uninstall opentelemetry-collector-logs -n telemetry + helm uninstall opentelemetry-collector-traces -n telemetry + ``` + +4. Uninstall the Prometheus stack. + ```sh + helm uninstall kube-prometheus-stack -n telemetry + ``` + +5. Remove the `telemetry` namespace. + ```sh + kubectl delete namespace telemetry + ``` \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/observability/tracing.md b/content/docs/kubernetes/1.1.x/observability/tracing.md new file mode 100644 index 000000000..1b976c932 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/observability/tracing.md @@ -0,0 +1,226 @@ +--- +title: Tracing +weight: 90 +test: + tracing: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/observability/tracing.md + path: tracing +--- + +Integrate your agentgateway proxy with an OpenTelemetry (OTel) collector and configure custom metadata for your traces with an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}}. + +{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} + +## Set up an OpenTelemetry collector + +Install an OpenTelemetry collector that the {{< reuse "agw-docs/snippets/agentgateway.md" >}} proxy can send traces to. Depending on your environment, you can further configure your OpenTelemetry to export these traces to your preferred tracing platform, such as Jaeger. + +1. Install the OTel collector. + ```sh {paths="tracing"} + helm upgrade --install opentelemetry-collector-traces opentelemetry-collector \ + --repo https://open-telemetry.github.io/opentelemetry-helm-charts \ + --version 0.127.2 \ + --set mode=deployment \ + --set image.repository="otel/opentelemetry-collector-contrib" \ + --set command.name="otelcol-contrib" \ + --namespace=telemetry \ + --create-namespace \ + -f -<}} + YAMLTest -f - <<'EOF' + - name: wait for OTel collector deployment to be ready + wait: + target: + kind: Deployment + metadata: + namespace: telemetry + name: opentelemetry-collector-traces + jsonPath: "$.status.availableReplicas" + jsonPathExpectation: + comparator: greaterThan + value: 0 + polling: + timeoutSeconds: 300 + intervalSeconds: 5 + EOF + {{< /doc-test >}} + +2. Verify that the collector is up and running. + ```sh + kubectl get pods -n telemetry + ``` + + Example output: + ```console + NAME READY STATUS RESTARTS AGE + opentelemetry-collector-traces-8f566f445-l82s6 1/1 Running 0 17m + ``` + +## Set up tracing + +1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource with your tracing configuration. + ```yaml {paths="tracing"} + kubectl apply -f- <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: tracing + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - kind: Gateway + name: agentgateway-proxy + group: gateway.networking.k8s.io + frontend: + tracing: + backendRef: + name: opentelemetry-collector-traces + namespace: telemetry + port: 4317 + protocol: GRPC + clientSampling: "true" + randomSampling: "true" + resources: + - name: deployment.environment.name + expression: '"production"' + - name: service.version + expression: '"test"' + attributes: + add: + - expression: 'request.headers["x-header-tag"]' + name: request + - expression: 'request.host' + name: host + EOF + ``` + +## Verify traces + +1. Send a request to the httpbin app with the `x-header-tag` header. + {{< tabs tabTotal="2" items="Cloud Provider LoadBalancer,Port-forward for local testing" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi -X POST http://$INGRESS_GW_ADDRESS:80/post \ + -H "host: www.example.com" \ + -H "x-header-tag: custom-tracing" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi -X POST localhost:8080/post \ + -H "host: www.example.com" \ + -H "x-header-tag: custom-tracing" + ``` + {{% /tab %}} + {{< /tabs >}} + + {{< doc-test paths="tracing" >}} + YAMLTest -f - <<'EOF' + - name: verify tracing setup - POST returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /post + method: POST + headers: + host: "www.example.com" + x-header-tag: custom-tracing + source: + type: local + expect: + statusCode: 200 + EOF + {{< /doc-test >}} + +2. Get the trace ID from your request from the agentgateway proxy logs. + ```sh + kubectl logs deploy/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} \ + | grep -o 'trace\.id=[^ ]*' | tail -1 + ``` + +3. Get the logs of the collector and search for the trace ID. Verify that you see the additional tracing attributes that you configured initially. + ```sh + kubectl logs deploy/opentelemetry-collector-traces -n telemetry \ + | grep -A 27 "Trace ID\s\+: " + ``` + + Example output: + ```console {hl_lines=[27,28]} + Trace ID : 2864d2f682a85ba0c44cb5122d2d11e5 + Parent ID : + ID : 947515b6316f7931 + Name : POST /* + Kind : Server + Start time : 2026-01-20 16:28:30.717325796 +0000 UTC + End time : 2026-01-20 16:28:30.717960087 +0000 UTC + Status code : Unset + Status message : + Attributes: + -> gateway: Str(agentgateway-system/agentgateway-proxy) + -> listener: Str(http) + -> route: Str(httpbin/httpbin) + -> endpoint: Str(10.244.0.31:8080) + -> src.addr: Str(127.0.0.1:50314) + -> http.method: Str(POST) + -> http.host: Str(www.example.com) + -> http.path: Str(/post) + -> http.version: Str(HTTP/1.1) + -> http.status: Int(200) + -> trace.id: Str(2864d2f682a85ba0c44cb5122d2d11e5) + -> span.id: Str(947515b6316f7931) + -> protocol: Str(http) + -> duration: Str(0ms) + -> url.scheme: Str(http) + -> network.protocol.version: Str(1.1) + -> request: Str(custom-tracing) + -> host: Str(www.example.com) + + ``` + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +1. Delete the {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource. + ```sh + kubectl delete {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} tracing -n {{< reuse "agw-docs/snippets/namespace.md" >}} + ``` + +2. Uninstall the OpenTelemetry collector. + ```sh + helm uninstall opentelemetry-collector-traces -n telemetry + ``` + +3. Remove the `telemetry` namespace. + ```sh + kubectl delete namespace telemetry + ``` \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/observability/ui.md b/content/docs/kubernetes/1.1.x/observability/ui.md new file mode 100644 index 000000000..a68cdd4c1 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/observability/ui.md @@ -0,0 +1,15 @@ +--- +title: Admin UI +weight: 10 +description: Use the built-in Admin UI to inspect your Kubernetes agentgateway proxy configuration. +test: + admin-ui: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/observability/ui.md + path: ui-k8s +--- + +{{< reuse "agw-docs/pages/observability/ui.md" >}} diff --git a/content/docs/kubernetes/1.1.x/operations/_index.md b/content/docs/kubernetes/1.1.x/operations/_index.md new file mode 100644 index 000000000..5765e1a68 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/operations/_index.md @@ -0,0 +1,9 @@ +--- +title: Operations +weight: 800 +icon: settings +test: skip +--- + +Manage your gateway lifecycle, such as upgrades. + diff --git a/content/docs/kubernetes/latest/operations/cli.txt b/content/docs/kubernetes/1.1.x/operations/cli.txt similarity index 100% rename from content/docs/kubernetes/latest/operations/cli.txt rename to content/docs/kubernetes/1.1.x/operations/cli.txt diff --git a/content/docs/kubernetes/1.1.x/operations/debug.md b/content/docs/kubernetes/1.1.x/operations/debug.md new file mode 100644 index 000000000..1dc623765 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/operations/debug.md @@ -0,0 +1,6 @@ +--- +title: Debug your setup +weight: 15 +--- + +{{< reuse "agw-docs/pages/operations/debug.md" >}} diff --git a/content/docs/kubernetes/1.1.x/operations/uninstall.md b/content/docs/kubernetes/1.1.x/operations/uninstall.md new file mode 100644 index 000000000..327c75338 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/operations/uninstall.md @@ -0,0 +1,13 @@ +--- +title: Uninstall +weight: 50 +description: Uninstall kgateway and related components. +test: + uninstall: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/operations/uninstall.md + path: uninstall +--- + +{{< reuse "agw-docs/pages/operations/uninstall.md" >}} diff --git a/content/docs/kubernetes/1.1.x/operations/upgrade.md b/content/docs/kubernetes/1.1.x/operations/upgrade.md new file mode 100644 index 000000000..6912d7543 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/operations/upgrade.md @@ -0,0 +1,13 @@ +--- +title: Upgrade +weight: 20 +description: Upgrade the control plane and any gateway proxies that run in your cluster. +test: + upgrade: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/operations/upgrade.md + path: upgrade +--- + +{{< reuse "agw-docs/pages/operations/upgrade.md" >}} diff --git a/content/docs/kubernetes/1.1.x/quickstart/_index.md b/content/docs/kubernetes/1.1.x/quickstart/_index.md new file mode 100644 index 000000000..acaca054b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/quickstart/_index.md @@ -0,0 +1,19 @@ +--- +linkTitle: "Get started" +title: Get started +weight: 1 +icon: rocket_launch +description: Install and run agentgateway, or route traffic to MCP, LLM, or HTTP backends. +test: skip +--- + +Get started with agentgateway on Kubernetes. Install the control plane, or jump to a quick start that routes traffic to the backend you need. + + +## Before you begin + +These guides assume you have a Kubernetes cluster, `kubectl`, and `helm`. For quick testing, you can use [Kind](https://kind.sigs.k8s.io/). + +```sh +kind create cluster +``` diff --git a/content/docs/kubernetes/1.1.x/quickstart/install.md b/content/docs/kubernetes/1.1.x/quickstart/install.md new file mode 100644 index 000000000..fc307efa6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/quickstart/install.md @@ -0,0 +1,11 @@ +--- +title: Install agentgateway +weight: 10 +description: Install the agentgateway control plane in your Kubernetes cluster. +test: + install: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard +--- + +{{< reuse "agw-docs/pages/agentgateway/quickstart/install.md" >}} diff --git a/content/docs/kubernetes/1.1.x/quickstart/llm.md b/content/docs/kubernetes/1.1.x/quickstart/llm.md new file mode 100644 index 000000000..24fc11e64 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/quickstart/llm.md @@ -0,0 +1,13 @@ +--- +title: LLM (OpenAI) +weight: 11 +description: Route requests to OpenAI's chat completions API with agentgateway on Kubernetes. +test: + openai: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/quickstart/llm.md + path: openai-setup +--- + +{{< reuse "agw-docs/pages/agentgateway/quickstart/llm.md" >}} diff --git a/content/docs/kubernetes/1.1.x/quickstart/mcp.md b/content/docs/kubernetes/1.1.x/quickstart/mcp.md new file mode 100644 index 000000000..70aa97aab --- /dev/null +++ b/content/docs/kubernetes/1.1.x/quickstart/mcp.md @@ -0,0 +1,13 @@ +--- +title: MCP servers +weight: 12 +description: Connect to an MCP server and try tools with agentgateway on Kubernetes. +test: + mcp: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/quickstart/mcp.md + path: setup-mcp-server +--- + +{{< reuse "agw-docs/pages/agentgateway/quickstart/mcp.md" >}} diff --git a/content/docs/kubernetes/1.1.x/quickstart/non-agentic-http.md b/content/docs/kubernetes/1.1.x/quickstart/non-agentic-http.md new file mode 100644 index 000000000..e8af1d408 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/quickstart/non-agentic-http.md @@ -0,0 +1,13 @@ +--- +title: Non-agentic HTTP +weight: 13 +description: Route HTTP traffic to a backend such as httpbin with agentgateway on Kubernetes. +test: + httpbin: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/quickstart/non-agentic-http.md + path: install-httpbin +--- + +{{< reuse "agw-docs/pages/agentgateway/quickstart/non-agentic-http.md" >}} diff --git a/content/docs/kubernetes/1.1.x/reference/_index.md b/content/docs/kubernetes/1.1.x/reference/_index.md new file mode 100644 index 000000000..a65b38486 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/_index.md @@ -0,0 +1,9 @@ +--- +title: Reference +weight: 901 +icon: menu_book +description: Review reference docs for the agentgateway API, Helm charts, version support, and more. +test: skip +--- + +Review reference docs for the API, Helm, version support,and more. diff --git a/content/docs/kubernetes/1.1.x/reference/api-kubespec/_index.md b/content/docs/kubernetes/1.1.x/reference/api-kubespec/_index.md new file mode 100644 index 000000000..58c963813 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/api-kubespec/_index.md @@ -0,0 +1,5 @@ +--- +title: API reference (explorer) +weight: 9 +description: Explore the interactive API reference for agentgateway custom resources. +--- diff --git a/content/docs/kubernetes/1.1.x/reference/api.md b/content/docs/kubernetes/1.1.x/reference/api.md new file mode 100644 index 000000000..eb8777bc2 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/api.md @@ -0,0 +1,12 @@ +--- +title: API reference (single page) +weight: 10 +description: Review the API reference for agentgateway custom resources. +test: skip +--- + +{{< reuse "/agw-docs/snippets/api-ref-docs-intro.md" >}} + +## Packages + +{{< reuse "/agw-docs/pages/reference/api/api-main.md" >}} diff --git a/content/docs/kubernetes/1.1.x/reference/cel/_index.md b/content/docs/kubernetes/1.1.x/reference/cel/_index.md new file mode 100644 index 000000000..09a8de8aa --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/cel/_index.md @@ -0,0 +1,11 @@ +--- +title: CEL expressions +weight: 15 +description: Learn how agentgateway uses CEL expressions to write flexible policies. +test: skip +--- + +Agentgateway uses the {{< gloss "CEL (Common Expression Language)" >}}CEL (Common Expression Language){{< /gloss >}} throughout the project to enable flexibility. +CEL allows writing simple expressions based on the request context that evaluate to some result. + +This section covers everything you need to write CEL expressions in agentgateway: how to embed expressions in YAML, common example patterns, and the variables and functions exposed at each policy phase. diff --git a/content/docs/kubernetes/1.1.x/reference/cel/variables.md b/content/docs/kubernetes/1.1.x/reference/cel/variables.md new file mode 100644 index 000000000..8829bfc98 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/cel/variables.md @@ -0,0 +1,42 @@ +--- +title: Variables and functions +weight: 3 +description: How CEL variables are populated per policy phase, and where to find the full context reference and function list. +test: skip +--- + +When using CEL expressions, a variety of variables and functions are made available. + +## Variables + +Variables are only available when they exist in the current context. Previously in version 0.11 or earlier, variables like `jwt` were always present but could be `null`. Now, to check if a JWT claim exists, use the expression `has(jwt.sub)`. This expression returns `false` if there is no JWT, rather than always returning `true`. + +Additionally, fields are populated only if they are referenced in a CEL expression. +This way, agentgateway avoids expensive buffering of request bodies if no CEL expression depends on the `body`. + +Each policy execution consistently gets the current view of the request and response. For example, during logging, any manipulations from earlier policies (such as transformations or external processing) are observable in the CEL context. + +For the full list of fields and types on every top-level object, see the [Interactive CEL reference]({{< link-hextra path="/reference/cel/cel-context-interactive" >}}) page. + +## Variables by policy type + +Depending on the policy, different fields are accessible based on when in the request processing they are applied. + +|Policy|Available Variables| +|------|-------------------| +|Transformation| `source`, `request`, `jwt`, `mcp`, `extauthz`, `response`, `llm` | +|Remote Rate Limit| `source`, `request`, `jwt` | +|HTTP Authorization| `source`, `request`, `jwt` | +|External Authorization| `source`, `request`, `jwt` | +|MCP Authorization| `source`, `request`, `jwt`, `mcp` | +|Logging| `source`, `request`, `jwt`, `mcp`, `extauthz`, `response`, `llm`| +|Tracing| `source`, `request`, `jwt`, `mcp`, `extauthz`, `response`, `llm`| +|Metrics| `source`, `request`, `jwt`, `mcp`, `extauthz`, `response`, `llm`| + +## Functions {#functions-policy-all} + +The following functions can be used in all policy types. + +{{% github-table url="https://raw.githubusercontent.com/agentgateway/agentgateway/refs/heads/main/schema/cel-functions.md" section="Functions" %}} + +{{% github-table url="https://raw.githubusercontent.com/agentgateway/agentgateway/refs/heads/main/schema/cel-functions.md" section="Standard Functions" %}} diff --git a/content/docs/kubernetes/1.1.x/reference/cel/yaml-and-examples.md b/content/docs/kubernetes/1.1.x/reference/cel/yaml-and-examples.md new file mode 100644 index 000000000..413531035 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/cel/yaml-and-examples.md @@ -0,0 +1,113 @@ +--- +title: CEL in YAML and example expressions +weight: 2 +description: How to embed CEL expressions in YAML configuration, plus worked examples for common use cases. +test: skip +--- + +## CEL in YAML + +When writing CEL expressions in agentgateway, typically they are expressed as YAML string values, which can cause confusion. +Any string literals within the expression need to be escaped with additional quotes. + +These examples all represent the same CEL expression, a string literal `"hello"`. + +```yaml +doubleQuote: "'hello'" +doubleQuoteEscaped: "\"hello\"" +singleQuote: '"hello"' +blockSingle: | + 'hello' +blockDouble: | + "hello" +``` + +These examples all represent the same CEL expression, a variable `request`: + +```yaml +doubleQuote: "request" +singleQuote: 'request' +block: | + request +``` + +The `block` style is often the most readable for complex expressions, and also allows for multi-line expressions without needing to escape quotes. + +## Example expressions + +Review the following examples of expressions for different use cases. + +### Default function {#default-function} + +You can use the `default` function to provide a fallback value if the expression cannot be resolved. + +Request header fallback with example of an anonymous user: + +``` +default(request.headers["x-user-id"], "anonymous") +``` + +Nested object fallback with example of light theme: + +``` +default(json(request.body).user.preferences.theme, "light") +``` + +{{< gloss "JWT (JSON Web Token)" >}}JWT{{< /gloss >}} claim fallback with example of default "user" role: + +``` +default(jwt.role, "user") +``` + +### Logs, traces, and observability {#logs} + +```yaml +# Include logs where there was no response or there was an error +filter: | + !has(response) || response.code > 299 +fields: + add: + user.agent: 'request.headers["user-agent"]' + # A static string. Note the expression is a string, and it returns a string, hence the double quotes. + span.name: '"openai.chat"' + gen_ai.usage.prompt_tokens: 'llm.input_tokens' + # Parse the JSON request body, and conditionally log... + # * If `type` is sum, val1+val2 + # * Else, val3 + # Example JSON request: `{"type":"sum","val1":2,"val2":3,"val4":"hello"}` + json.field: | + json(request.body).with(b, + b.type == "sum" ? + b.val1 + b.val2 : + b.val3 + ) + +``` + +### Authorization {#authorization} + +```yaml +mcpAuthorization: + rules: + # Allow anyone to call 'echo' + - 'mcp.tool.name == "echo"' + # Only the test-user can call 'add' + - 'jwt.sub == "test-user" && mcp.tool.name == "add"' + # Any authenticated user with the claim `nested.key == value` can access 'printEnv' + - 'mcp.tool.name == "printEnv" && jwt.nested.key == "value"' +``` + +### Rate limiting {#rate-limiting} + +```yaml +remoteRateLimit: + descriptors: + # Rate limit requests based on a header, whether the user is authenticated, and a static value (used to match a specific rate limit rule on the rate limit server) + - entries: + - key: some-static-value + value: '"something"' + - key: organization + value: 'request.headers["x-organization"]' + - key: authenticated + value: 'has(jwt.sub)' +``` diff --git a/content/docs/kubernetes/1.1.x/reference/helm/_index.md b/content/docs/kubernetes/1.1.x/reference/helm/_index.md new file mode 100644 index 000000000..3c666dd9f --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/helm/_index.md @@ -0,0 +1,29 @@ +--- +title: Helm value reference +weight: 20 +description: Review Helm values for agentgateway Helm charts. +--- + +## Download the Helm chart {#download} + +You can download the Helm chart to review the Helm values that are supported. + +1. Download the Helm chart as an archive to your local machine. + ```sh + helm pull {{< reuse "agw-docs/snippets/helm-path.md" >}} --version v{{< reuse "agw-docs/versions/n-patch.md" >}} + ``` + +2. Extract the files from the downloaded archive. + ```sh + tar -xvf agentgateway-v{{< reuse "agw-docs/versions/n-patch.md" >}}.tgz + ``` + +3. Open the Helm values file. + ```sh + open agentgateway/values.yaml + ``` + +## Helm charts + +Review the documentation for the following Helm charts. + diff --git a/content/docs/kubernetes/1.1.x/reference/helm/agentgateway-crds..md b/content/docs/kubernetes/1.1.x/reference/helm/agentgateway-crds..md new file mode 100644 index 000000000..9ea23c598 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/helm/agentgateway-crds..md @@ -0,0 +1,12 @@ +--- +title: agentgateway-crds +weight: 30 +description: Review Helm values for the agentgateway-crds Helm chart. +test: skip +--- + +Review Helm values for the agentgateway-crds Helm chart. + +This Helm chart controls whether specific sets of CRDs for agentgateway are installed. For more information about using this Helm chart, see the [Helm installation guide]({{< link-hextra path="/install/helm" >}}). + +{{< reuse "agw-docs/pages/reference/helm/main/agentgateway-crds.md" >}} diff --git a/content/docs/kubernetes/1.1.x/reference/helm/agentgateway.md b/content/docs/kubernetes/1.1.x/reference/helm/agentgateway.md new file mode 100644 index 000000000..98594d0c1 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/helm/agentgateway.md @@ -0,0 +1,12 @@ +--- +title: agentgateway +weight: 30 +description: Review Helm values for the agentgateway Helm chart. +test: skip +--- + +Review Helm values for the agentgateway Helm chart. + +For more information about using this Helm chart, see the [Helm installation guide]({{< link-hextra path="/install/helm" >}}). + +{{< reuse "agw-docs/pages/reference/helm/main/agentgateway.md" >}} diff --git a/content/docs/kubernetes/1.1.x/reference/release-notes.md b/content/docs/kubernetes/1.1.x/reference/release-notes.md new file mode 100644 index 000000000..55683750b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/release-notes.md @@ -0,0 +1,109 @@ +--- +title: Release notes +weight: 20 +description: Review the release notes for agentgateway. +test: skip +--- + +Review the release notes for agentgateway. + +{{< callout type="info">}} +For more details, review the [GitHub release notes in the agentgateway repository](https://github.com/agentgateway/agentgateway/releases) +{{< /callout >}} + +## πŸ”₯ Breaking changes {#v11-breaking-changes} + +### MCP authentication moved to route level + + + +MCP authentication is configured at the traffic (route) level using `traffic.jwtAuthentication` with the `mcp` extension field, instead of the previous `backend.mcp.authentication` field. The route-level placement aligns MCP auth with standard JWT authentication and allows JWT claims to be used in other traffic policies such as authorization, rate limiting, and transformations. + +* **Before**: MCP authentication was configured under `backend.mcp.authentication`, targeting an {{< reuse "agw-docs/snippets/agentgateway/agentgatewaybackend.md" >}}. This previous `backend.mcp.authentication` field is deprecated but continues to work for backward compatibility. If both are set on the same route, the backend-level configuration is ignored. +* **After**: MCP authentication is configured under `traffic.jwtAuthentication` with an `mcp` field, targeting an HTTPRoute, such as in the following example. For more information, see [Set up MCP auth]({{< link-hextra path="/mcp/auth/setup/" >}}). + +```yaml +apiVersion: {{< reuse "agw-docs/snippets/trafficpolicy-apiversion.md" >}} +kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} +metadata: + name: mcp-authn +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: mcp + traffic: + jwtAuthentication: + mode: Strict + providers: + - issuer: http://keycloak.example.com/realms/myrealm + audiences: + - http://localhost:8080/mcp + jwks: + remote: + backendRef: + name: keycloak + kind: Service + namespace: keycloak + port: 8080 + jwksPath: /realms/master/protocol/openid-connect/certs + mcp: + provider: Keycloak + resourceMetadata: + resource: http://localhost:8080/mcp + scopesSupported: + - email + bearerMethodsSupported: + - header +``` + +## 🌟 New features {#v11-new-features} + +### Network authorization + +A new `networkAuthorization` field in the `frontend` policy section enables Layer 4 network authorization based on source IP, port, and mTLS client identity. You can enforce policies for non-HTTP traffic and layer L4+L7 controls. For more information, see [Policies]({{< link-hextra path="/about/policies/" >}}). + +### Authorization require rules + +Authorization policies now support `Require` as an action in addition to `Allow` and `Deny`. All `Require` rules must match for the request to proceed, providing clearer semantics than double-negative deny rules. For more information, see [Policies]({{< link-hextra path="/about/policies/" >}}). + +### MCP improvements + +- **Stateless sessions**: MCP upstreams can now use stateless sessions. For more information, see [Stateful MCP]({{< link-hextra path="/mcp/session/" >}}). +- **Explicit service reference lists**: MCP backends can specify targets with explicit service references. For more information, see [Static MCP]({{< link-hextra path="/mcp/static-mcp/" >}}). +- **Tool payloads in CEL context**: Tool names and payloads are available in logging CEL expressions. + +### LLM gateway enhancements + +- **Path prefixes**: LLM providers now support path prefixes for custom API base paths. +- **Azure default authentication**: Azure OpenAI providers can use platform-default authentication. For more information, see [Azure OpenAI]({{< link-hextra path="/llm/providers/azure/" >}}). +- **Vertex region optional**: Vertex AI region configuration is now optional with a global default. For more information, see [Vertex AI]({{< link-hextra path="/llm/providers/vertex/" >}}). + +### Gateway and routing improvements + +- **Automatic protocol detection**: A new `auto` bind protocol auto-detects TLS vs HTTP connections. +- **Service SANs for upstream TLS**: Upstream TLS now respects Subject Alternative Names from Kubernetes Services. For more information, see [BackendTLS]({{< link-hextra path="/security/backendtls/" >}}). +- **TLSRoute v1 status**: Status is now written using the `TLSRoute v1` API version. +- **CEL hash functions**: New `sha1.encode`, `sha256.encode`, and `md5.encode` functions are available in CEL expressions. + +## πŸ—‘οΈ Deprecated or removed features + +### MCP authentication on backend AgentgatewayPolicy + +As described in the breaking changes section, MCP authentication is now configured at the route level using `traffic.jwtAuthentication` with the `mcp` extension field, instead of the previous `backend.mcp.authentication` field. + +The `backend.mcp.authentication` field on the AgentgatewayPolicy resource is deprecated and will be removed in a future release. + +### MCP policy on AgentgatewayBackend + + + +Previously, AgentgatewayBackend resources had fields for `spec.mcp.targets.static.policies.mcp.{authentication,authorization}`. + +These fields were not intended to be set, and had no impact on the behavior of the proxy. + +As such, these fields are now removed. + +If you previously set these fields which had no behavioral impact and were ignored, the configuration now fails to be applied. + +Instead, use the `jwtAuthentication.mcp` field on the AgentgatewayPolicy resource, which ensures authentication runs before other policies such as transformation and rate limiting. diff --git a/content/docs/kubernetes/1.1.x/reference/versions.md b/content/docs/kubernetes/1.1.x/reference/versions.md new file mode 100644 index 000000000..daf3d1e27 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/reference/versions.md @@ -0,0 +1,10 @@ +--- +title: Version support +description: View supported versions and their release cycle. +weight: 35 +test: skip +--- + +{{< reuse "agw-docs/pages/reference/versions.md" >}} + +# {#kubernetes} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/resiliency/_index.md b/content/docs/kubernetes/1.1.x/resiliency/_index.md new file mode 100644 index 000000000..8a7c31120 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/_index.md @@ -0,0 +1,6 @@ +--- +title: Resiliency +weight: 72 +icon: health_and_safety +test: skip +--- diff --git a/content/docs/kubernetes/1.1.x/resiliency/connection.md b/content/docs/kubernetes/1.1.x/resiliency/connection.md new file mode 100644 index 000000000..f3b25dedf --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/connection.md @@ -0,0 +1,37 @@ +--- +title: HTTP connection settings +weight: 10 +description: Configure and manage HTTP connections to an upstream service. +test: + connection-general: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/connection.md + path: connection-general + + connection-http1: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/connection.md + path: connection-http1 + + connection-http2-flow: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/connection.md + path: connection-http2-flow +--- + +{{< reuse "agw-docs/pages/resiliency/connection.md" >}} diff --git a/content/docs/kubernetes/1.1.x/resiliency/keepalive.md b/content/docs/kubernetes/1.1.x/resiliency/keepalive.md new file mode 100644 index 000000000..94b438a93 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/keepalive.md @@ -0,0 +1,27 @@ +--- +title: Keepalive +weight: 10 +description: Manage idle and stale connections with TCP and HTTP keepalive. +test: + tcp-keepalive: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/keepalive.md + path: tcp-keepalive + + http-keepalive: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/keepalive.md + path: http-keepalive +--- + +{{< reuse "agw-docs/pages/resiliency/keepalive.md" >}} diff --git a/content/docs/kubernetes/1.1.x/resiliency/retry/_index.md b/content/docs/kubernetes/1.1.x/resiliency/retry/_index.md new file mode 100644 index 000000000..2cfa65689 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/retry/_index.md @@ -0,0 +1,6 @@ +--- +title: Retries +weight: 10 +description: Configure retries. +test: skip +--- diff --git a/content/docs/kubernetes/1.1.x/resiliency/retry/about.md b/content/docs/kubernetes/1.1.x/resiliency/retry/about.md new file mode 100644 index 000000000..f33c4aa5a --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/retry/about.md @@ -0,0 +1,8 @@ +--- +title: About +weight: 10 +description: Learn about retries. +test: skip +--- + +{{< reuse "agw-docs/pages/resiliency/retry/about.md" >}} diff --git a/content/docs/kubernetes/1.1.x/resiliency/retry/per-try-timeout.md b/content/docs/kubernetes/1.1.x/resiliency/retry/per-try-timeout.md new file mode 100644 index 000000000..9a007e2bf --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/retry/per-try-timeout.md @@ -0,0 +1,35 @@ +--- +title: Per-try timeout +weight: 20 +description: Set up per-try timeouts. +test: + per-try-timeout-in-httproute: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/retry/per-try-timeout.md + path: per-try-timeout-in-httproute + per-try-timeout-in-agentgateway: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/retry/per-try-timeout.md + path: per-try-timeout-in-agentgateway + per-try-timeout-in-gatewaylistener: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/retry/per-try-timeout.md + path: per-try-timeout-in-gatewaylistener +--- + +{{< reuse "agw-docs/pages/resiliency/retry/per-try-timeout.md" >}} diff --git a/content/docs/kubernetes/1.1.x/resiliency/retry/retry.md b/content/docs/kubernetes/1.1.x/resiliency/retry/retry.md new file mode 100644 index 000000000..0b3e1280a --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/retry/retry.md @@ -0,0 +1,35 @@ +--- +title: Request retries +weight: 10 +description: Set up retries for requests. +test: + retry-in-httproute: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/retry/retry.md + path: retry-in-httproute + retry-in-agentgateway: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/retry/retry.md + path: retry-in-agentgateway + retry-in-gatewaylistener: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/retry/retry.md + path: retry-in-gatewaylistener +--- + +{{< reuse "agw-docs/pages/resiliency/retry/retry.md" >}} diff --git a/content/docs/kubernetes/1.1.x/resiliency/timeouts/_index.md b/content/docs/kubernetes/1.1.x/resiliency/timeouts/_index.md new file mode 100644 index 000000000..fbe796293 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/timeouts/_index.md @@ -0,0 +1,7 @@ +--- +title: Timeouts +weight: 10 +description: Configure timeouts. +test: skip +--- + diff --git a/content/docs/kubernetes/1.1.x/resiliency/timeouts/about.md b/content/docs/kubernetes/1.1.x/resiliency/timeouts/about.md new file mode 100644 index 000000000..a0fc0a4d5 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/timeouts/about.md @@ -0,0 +1,8 @@ +--- +title: About timeouts +weight: 5 +description: +test: skip +--- + +{{< reuse "agw-docs/pages/resiliency/timeouts/about.md" >}} diff --git a/content/docs/kubernetes/1.1.x/resiliency/timeouts/idle.md b/content/docs/kubernetes/1.1.x/resiliency/timeouts/idle.md new file mode 100644 index 000000000..0f5ad26a3 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/timeouts/idle.md @@ -0,0 +1,17 @@ +--- +title: Idle timeouts +weight: 20 +description: +test: + idle-timeout: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/timeouts/idle.md + path: idle-timeout +--- + +{{< reuse "agw-docs/pages/resiliency/timeouts/idle.md" >}} diff --git a/content/docs/kubernetes/1.1.x/resiliency/timeouts/per-try-timeout.md b/content/docs/kubernetes/1.1.x/resiliency/timeouts/per-try-timeout.md new file mode 100644 index 000000000..ecb90e134 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/timeouts/per-try-timeout.md @@ -0,0 +1,11 @@ +--- +title: Per-try timeouts +weight: 30 +description: +test: skip +--- + +{{< cards >}} +{{< card path="/resiliency/retry/per-try-timeout/" title="Per-try timeouts" >}} +{{< /cards >}} + diff --git a/content/docs/kubernetes/1.1.x/resiliency/timeouts/request.md b/content/docs/kubernetes/1.1.x/resiliency/timeouts/request.md new file mode 100644 index 000000000..08c523be6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/resiliency/timeouts/request.md @@ -0,0 +1,35 @@ +--- +title: Request timeouts +weight: 10 +description: Configure timeouts for all routes in an HTTPRoute. +test: + timeout-in-httproute: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/timeouts/request.md + path: timeout-in-httproute + timeout-in-trafficpolicy: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/timeouts/request.md + path: timeout-in-trafficpolicy + timeout-in-gatewaylistener: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/resiliency/timeouts/request.md + path: timeout-in-gatewaylistener +--- + +{{< reuse "agw-docs/pages/resiliency/timeouts/request.md" >}} diff --git a/content/docs/kubernetes/1.1.x/security/_index.md b/content/docs/kubernetes/1.1.x/security/_index.md new file mode 100644 index 000000000..fa649cd7b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/_index.md @@ -0,0 +1,6 @@ +--- +title: Security +weight: 75 +icon: security +test: skip +--- diff --git a/content/docs/kubernetes/1.1.x/security/access-logging.md b/content/docs/kubernetes/1.1.x/security/access-logging.md new file mode 100644 index 000000000..b0775f722 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/access-logging.md @@ -0,0 +1,17 @@ +--- +title: Access logging +weight: 10 +description: Capture an access log for all the requests that enter the proxy. +test: + access-logging: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/security/access-logging.md + path: access-logging +--- + +{{< reuse "agw-docs/pages/security/access-logging.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/security/backendtls.md b/content/docs/kubernetes/1.1.x/security/backendtls.md new file mode 100644 index 000000000..ff0ceffca --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/backendtls.md @@ -0,0 +1,7 @@ +--- +title: BackendTLS +weight: 10 +description: +--- + +{{< reuse "agw-docs/pages/security/backendtls.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/security/cors.md b/content/docs/kubernetes/1.1.x/security/cors.md new file mode 100644 index 000000000..af0e5c6dc --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/cors.md @@ -0,0 +1,27 @@ +--- +title: CORS +weight: 10 +test: + cors-in-httproute: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/security/cors.md + path: cors-in-httproute + + cors-in-agentgatewaypolicy: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/security/cors.md + path: cors-in-agentgatewaypolicy +--- + +{{< reuse "agw-docs/pages/security/cors.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/security/csrf.md b/content/docs/kubernetes/1.1.x/security/csrf.md new file mode 100644 index 000000000..9566926c5 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/csrf.md @@ -0,0 +1,17 @@ +--- +title: CSRF +weight: 10 +description: Protect your applications from Cross-Site Request Forgery (CSRF) attacks. +test: + csrf: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/security/csrf.md + path: csrf +--- + +{{< reuse "agw-docs/pages/security/csrf.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/security/extauth/_index.md b/content/docs/kubernetes/1.1.x/security/extauth/_index.md new file mode 100644 index 000000000..249de802e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/extauth/_index.md @@ -0,0 +1,7 @@ +--- +title: External auth +weight: 10 +description: +test: skip +--- + diff --git a/content/docs/kubernetes/latest/security/extauth/apikey.md b/content/docs/kubernetes/1.1.x/security/extauth/apikey.md similarity index 100% rename from content/docs/kubernetes/latest/security/extauth/apikey.md rename to content/docs/kubernetes/1.1.x/security/extauth/apikey.md diff --git a/content/docs/kubernetes/1.1.x/security/extauth/basic.md b/content/docs/kubernetes/1.1.x/security/extauth/basic.md new file mode 100644 index 000000000..bf31dc9e6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/extauth/basic.md @@ -0,0 +1,7 @@ +--- +title: Basic auth +weight: 10 +description: Set up basic auth to secure requests to an LLM, MCP server, or agent. +--- + +{{< reuse "agw-docs/pages/security/basic-auth.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/security/extauth/byo-ext-auth-service.md b/content/docs/kubernetes/1.1.x/security/extauth/byo-ext-auth-service.md new file mode 100644 index 000000000..a7cc08242 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/extauth/byo-ext-auth-service.md @@ -0,0 +1,9 @@ +--- +title: BYO ext auth service +weight: 100 +description: +--- + +{{< reuse "agw-docs/pages/security/byo-extauth.md" >}} + + diff --git a/content/docs/kubernetes/1.1.x/security/jwt/_index.md b/content/docs/kubernetes/1.1.x/security/jwt/_index.md new file mode 100644 index 000000000..44e89aac9 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/jwt/_index.md @@ -0,0 +1,7 @@ +--- +title: JWT auth +weight: 10 +description: +test: skip +--- + diff --git a/content/docs/kubernetes/1.1.x/security/jwt/about.md b/content/docs/kubernetes/1.1.x/security/jwt/about.md new file mode 100644 index 000000000..817bdc796 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/jwt/about.md @@ -0,0 +1,8 @@ +--- +title: About JWT auth +weight: 10 +description: +test: skip +--- + +{{< reuse "agw-docs/pages/security/jwt-about.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/security/jwt/mcp.md b/content/docs/kubernetes/1.1.x/security/jwt/mcp.md new file mode 100644 index 000000000..d344768a2 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/jwt/mcp.md @@ -0,0 +1,7 @@ +--- +title: JWT auth for MCP services +weight: 15 +description: Redirects to the MCP JWT authorization guide. +--- + +{{< redirect path="/mcp/mcp-access/" >}} diff --git a/content/docs/kubernetes/1.1.x/security/jwt/setup.md b/content/docs/kubernetes/1.1.x/security/jwt/setup.md new file mode 100644 index 000000000..fbd7fce04 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/jwt/setup.md @@ -0,0 +1,6 @@ +--- +title: JWT auth +weight: 10 +--- + +{{< reuse "agw-docs/pages/security/jwt-setup.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/security/rate-limit-global.md b/content/docs/kubernetes/1.1.x/security/rate-limit-global.md new file mode 100644 index 000000000..649bfe132 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/rate-limit-global.md @@ -0,0 +1,17 @@ +--- +title: Global rate limiting +weight: 45 +description: Apply distributed rate limits across multiple agentgateway replicas using an external rate limit service. +test: + global-rate-limit-by-ip: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/security/rate-limit-global.md + path: global-rate-limit-by-ip +--- + +{{< reuse "agw-docs/pages/security/rate-limit-global.md" >}} diff --git a/content/docs/kubernetes/1.1.x/security/rate-limit-http.md b/content/docs/kubernetes/1.1.x/security/rate-limit-http.md new file mode 100644 index 000000000..67b9d7fca --- /dev/null +++ b/content/docs/kubernetes/1.1.x/security/rate-limit-http.md @@ -0,0 +1,17 @@ +--- +title: Local rate limiting +weight: 40 +description: Apply local and global rate limits to HTTP traffic to protect your backend services from overload. +test: + local-rate-limit: + - file: content/docs/kubernetes/latest/install/helm.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/security/rate-limit-http.md + path: local-rate-limit +--- + +{{< reuse "agw-docs/pages/security/rate-limit-http.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/_index.md b/content/docs/kubernetes/1.1.x/setup/_index.md new file mode 100644 index 000000000..04bed4a02 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/_index.md @@ -0,0 +1,7 @@ +--- +title: Gateway setup +weight: 15 +icon: dns +description: +test: skip +--- diff --git a/content/docs/kubernetes/1.1.x/setup/customize/_index.md b/content/docs/kubernetes/1.1.x/setup/customize/_index.md new file mode 100644 index 000000000..cede2f64d --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/customize/_index.md @@ -0,0 +1,7 @@ +--- +title: Customize the gateway +weight: 30 +description: +test: skip +--- + diff --git a/content/docs/kubernetes/1.1.x/setup/customize/configs.md b/content/docs/kubernetes/1.1.x/setup/customize/configs.md new file mode 100644 index 000000000..1227d50e8 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/customize/configs.md @@ -0,0 +1,7 @@ +--- +title: Example configs +weight: 30 +description: +--- + +{{< reuse "agw-docs/pages/setup/customize-examples.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/setup/customize/customize.md b/content/docs/kubernetes/1.1.x/setup/customize/customize.md new file mode 100644 index 000000000..01026f170 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/customize/customize.md @@ -0,0 +1,7 @@ +--- +title: Customize the gateway +weight: 20 +description: +--- + +{{< reuse "agw-docs/pages/setup/customize.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/setup/customize/options.md b/content/docs/kubernetes/1.1.x/setup/customize/options.md new file mode 100644 index 000000000..408ceeb39 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/customize/options.md @@ -0,0 +1,7 @@ +--- +title: Customization options +weight: 10 +description: +--- + +{{< reuse "agw-docs/pages/setup/customize-options.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/setup/gateway.md b/content/docs/kubernetes/1.1.x/setup/gateway.md new file mode 100644 index 000000000..5ed5c2082 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/gateway.md @@ -0,0 +1,114 @@ +--- +title: Set up an agentgateway proxy +weight: 10 +description: +test: + setup-gateway: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all +--- + +Set up an agentgateway proxy. + +## Before you begin + +{{< reuse "agw-docs/snippets/agentgateway-prereq.md" >}} + +## Set up an agentgateway proxy + +{{< reuse "agw-docs/snippets/agentgateway-setup.md" >}} + +{{< doc-test paths="all" >}} +YAMLTest -f - <<'EOF' +- name: wait for agentgateway-proxy deployment to be ready + wait: + target: + kind: Deployment + metadata: + namespace: agentgateway-system + name: agentgateway-proxy + jsonPath: "$.status.availableReplicas" + jsonPathExpectation: + comparator: greaterThan + value: 0 + polling: + timeoutSeconds: 300 + intervalSeconds: 5 + +- name: wait for agentgateway-proxy service LB address + wait: + target: + kind: Service + metadata: + namespace: agentgateway-system + name: agentgateway-proxy + jsonPath: "$.status.loadBalancer.ingress[0].ip" + jsonPathExpectation: + comparator: exists + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +EOF +{{< /doc-test >}} + + +## Next + +Explore how you can use {{< reuse "agw-docs/snippets/agentgateway.md" >}} by checking out guides for the most common use cases: +* [LLM consumption]({{< link-hextra path="/llm" >}}) +* [Inference routing]({{< link-hextra path="/inference" >}}) +* [MCP connectivity]({{< link-hextra path="/mcp" >}}) +* [Agent connectivity]({{< link-hextra path="/agent" >}}) + +You can also install the [httpbin sample app]({{< link-hextra path="/install/sample-app/" >}}) and use this app to test traffic management, security, and resiliency guides with your agentgateway proxy. + +## Other configuration examples + +Review other common configuration examples for your Gateway. To customize your Gateway even further, such as with Kubernetes overlays, check out [Customize the gateway]({{< link-hextra path="/setup/customize" >}}). + +### Static IP address + +You can assign a static IP address to the service that exposes your gateway proxy by using the `spec.addresses` field. + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/gatewayclass.md" >}} + addresses: + - type: IPAddress + value: 203.0.113.11 + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: Same +``` + +### Same namespace HTTPRoutes + +Allow only HTTPRoutes that are in the same namespace as the Gateway to be attached to this Gateway (`allowedRoutes.namespaces.from=Same`). + +```yaml +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: Same +``` \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/_index.md b/content/docs/kubernetes/1.1.x/setup/listeners/_index.md new file mode 100644 index 000000000..c31ed3ef4 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/_index.md @@ -0,0 +1,5 @@ +--- +title: Listeners +weight: 20 +test: skip +--- diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/http.md b/content/docs/kubernetes/1.1.x/setup/listeners/http.md new file mode 100644 index 000000000..8967f7fc3 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/http.md @@ -0,0 +1,16 @@ +--- +title: HTTP +weight: 10 +test: + http-listener: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/setup/listeners/http.md + path: http-listener +--- + +{{< reuse "agw-docs/pages/setup/listeners/http.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/https.md b/content/docs/kubernetes/1.1.x/setup/listeners/https.md new file mode 100644 index 000000000..26eeed428 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/https.md @@ -0,0 +1,6 @@ +--- +title: HTTPS +weight: 10 +--- + +{{< reuse "agw-docs/pages/setup/listeners/https.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/mtls.md b/content/docs/kubernetes/1.1.x/setup/listeners/mtls.md new file mode 100644 index 000000000..e91b64054 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/mtls.md @@ -0,0 +1,7 @@ +--- +title: mTLS (FrontendTLS) +weight: 10 +description: +--- + +{{< reuse "agw-docs/pages/setup/listeners/listener-mtls.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/overview.md b/content/docs/kubernetes/1.1.x/setup/listeners/overview.md new file mode 100644 index 000000000..d8385b055 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/overview.md @@ -0,0 +1,7 @@ +--- +title: Listener overview +weight: 5 +description: +--- + +{{< reuse "agw-docs/pages/setup/listeners/overview.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/sni.txt b/content/docs/kubernetes/1.1.x/setup/listeners/sni.txt new file mode 100644 index 000000000..db4ae7ec6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/sni.txt @@ -0,0 +1,7 @@ +--- +title: SNI +weight: 10 +description: +--- + +{{< reuse "agw-docs/pages/setup/listeners/sni.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/tcp.md b/content/docs/kubernetes/1.1.x/setup/listeners/tcp.md new file mode 100644 index 000000000..b80ffadd1 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/tcp.md @@ -0,0 +1,6 @@ +--- +title: TCP +weight: 10 +--- + +{{< reuse "agw-docs/pages/setup/listeners/tcp.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/tls-passthrough.txt b/content/docs/kubernetes/1.1.x/setup/listeners/tls-passthrough.txt new file mode 100644 index 000000000..e17bf37ff --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/tls-passthrough.txt @@ -0,0 +1,7 @@ +--- +title: TLS passthrough +weight: 10 +description: Set up a TLS listener on the Gateway that serves one or more hosts and passes TLS traffic through to a destination. +--- + +{{< reuse "agw-docs/pages/setup/listeners/tls-passthrough.md" >}} diff --git a/content/docs/kubernetes/1.1.x/setup/listeners/tls-settings.md b/content/docs/kubernetes/1.1.x/setup/listeners/tls-settings.md new file mode 100644 index 000000000..aa6bde742 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/setup/listeners/tls-settings.md @@ -0,0 +1,6 @@ +--- +title: Additional TLS settings +weight: 20 +--- + +{{< reuse "agw-docs/pages/setup/listeners/tls-settings.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/traffic-management/_index.md b/content/docs/kubernetes/1.1.x/traffic-management/_index.md new file mode 100644 index 000000000..acbd303e6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/_index.md @@ -0,0 +1,8 @@ +--- +title: Traffic management +weight: 70 +icon: traffic +test: skip +--- + +Control traffic with external processing, request matching, redirects, transformations, and traffic splitting. diff --git a/content/docs/kubernetes/1.1.x/traffic-management/buffering.md b/content/docs/kubernetes/1.1.x/traffic-management/buffering.md new file mode 100644 index 000000000..384633cfa --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/buffering.md @@ -0,0 +1,15 @@ +--- +title: Buffering +weight: 10 +description: +test: + buffering: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/traffic-management/buffering.md + path: buffering +--- + +{{< reuse "agw-docs/pages/traffic-management/buffering.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/dfp.md b/content/docs/kubernetes/1.1.x/traffic-management/dfp.md new file mode 100644 index 000000000..72562f1b7 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/dfp.md @@ -0,0 +1,17 @@ +--- +title: Dynamic Forward Proxy (DFP) +weight: 10 +description: +test: + dfp: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/dfp.md + path: dfp +--- + +{{< reuse "agw-docs/pages/traffic-management/dfp.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/traffic-management/direct-response.md b/content/docs/kubernetes/1.1.x/traffic-management/direct-response.md new file mode 100644 index 000000000..6380ec88e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/direct-response.md @@ -0,0 +1,17 @@ +--- +title: Direct responses +weight: 10 +description: +test: + direct-response: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/direct-response.md + path: direct-response +--- + +{{< reuse "agw-docs/pages/traffic-management/direct-response.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/extproc.md b/content/docs/kubernetes/1.1.x/traffic-management/extproc.md new file mode 100644 index 000000000..bb1253eb5 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/extproc.md @@ -0,0 +1,249 @@ +--- +title: External processing (ExtProc) +weight: 10 +description: +test: + extproc: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/extproc.md + path: extproc +--- + +Modify aspects of an HTTP request or response with an external processing server. + +## About external processing + +With the external processing, you can implement an external gRPC processing server that can read and modify all aspects of an HTTP request or response, and add that server to the agentgateway proxy processing chain. The external service can manipulate headers, body, and trailers of a request or response before it is forwarded to an upstream or downstream service. The request or response can also be terminated at any given time. + +With this approach, you have the flexibility to apply your requirements to all types of apps, without the need to run WebAssembly or other custom scripts. + +### How it works + +The following diagram shows how request header manipulation works when an external processing server is used. + +```mermaid +sequenceDiagram + participant Downstream as Downstream Service + participant Gateway as Gateway + participant ExtProc as ExtProc Server + participant Upstream as Upstream Application + + Downstream->>Gateway: 1. Send request with headers + + Gateway->>ExtProc: 2. Extract and send header information + + ExtProc->>ExtProc: 3. Modify, add, or remove headers + + ExtProc-->>Gateway: 4. Return modified headers + + Gateway->>Gateway: 5. Add modified headers to request + + Gateway->>Upstream: 6. Forward request to upstream + + Upstream-->>Gateway: 7. Return response + + Gateway->>Downstream: 8. Forward response +``` + +### ExtProc server considerations + +The ExtProc server is a gRPC interface that must be able to respond to events in the lifecycle of an HTTP request. When ExtProc is enabled and a request or response is received on the gateway proxy, the proxy communicates with the ExtProc server by using bidirectional gRPC streams. + +To implement your own ExtProc server, make sure that you follow [Envoy's technical specification for an external processor](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto#extensions-filters-http-ext-proc-v3-externalprocessor). This guide uses a sample ExtProc server that you can use to try out the ExtProc functionality. + +{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} + +## Set up an ExtProc server + +Use a sample ExtProc server implementation to try out the ExtProc functionality in {{< reuse "agw-docs/snippets/kgateway.md" >}}. + +1. Set up the ExtProc server. This example uses a prebuilt ExtProc server that manipulates request and response headers based on instructions that are sent in an instructions header. + ```yaml {paths="extproc"} + kubectl apply -n {{< reuse "agw-docs/snippets/namespace.md" >}} -f- <}} +YAMLTest -f - <<'EOF' +- name: wait for ext-proc-grpc deployment to be ready + wait: + target: + kind: Deployment + metadata: + namespace: agentgateway-system + name: ext-proc-grpc + jsonPath: "$.status.availableReplicas" + jsonPathExpectation: + comparator: greaterThan + value: 0 + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +EOF +{{< /doc-test >}} + +2. Verify that the ExtProc server is up and running. + ```sh + kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} | grep ext-proc-grpc + ``` + + +## Set up ExtProc + +You can enable ExtProc for a particular route in an HTTPRoute resource. + +1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} that enables external processing for the agentgateway-proxy. + ```yaml {paths="extproc"} + kubectl apply -f- <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: extproc + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: agentgateway-proxy + traffic: + extProc: + backendRef: + name: ext-proc-grpc + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + port: 4444 + EOF + ``` + +{{< doc-test paths="extproc" >}} +YAMLTest -f - <<'EOF' +- name: extproc - /headers returns 200 with extproc header injected + retries: 10 + http: + url: "http://${INGRESS_GW_ADDRESS}:80/headers" + method: GET + headers: + host: www.example.com + instructions: '{"addHeaders":{"extproc":"true"}}' + source: + type: local + expect: + statusCode: 200 + bodyJsonPath: + - path: "$.headers.Extproc[0]" + comparator: equals + value: "true" +EOF +{{< /doc-test >}} + +2. Send a request to the httpbin app along the `/headers` path and provide your instructions in the `instruction` header. This example instructs the ExtProc server to add the `extproc: true` header. Verify that you get back a 200 HTTP response and that your response includes the `extproc: true` header. + + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:8080/headers -H "host: www.example.com" -H 'instructions: {"addHeaders":{"extproc":"true"}}' + ``` + {{% /tab %}} + + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi http://localhost:8080/headers -H "host: www.example.com" -H 'instructions: {"addHeaders":{"extproc":"true"}}' + ``` + {{% /tab %}} + + {{< /tabs >}} + + Example output: + + ```console {hl_lines=[10,11]} + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + ... + < + { + "headers": { + "Accept": [ + "*/*" + ], + "Extproc": [ + "true" + ], + "Host": [ + "www.example.com" + ], + "Instructions": [ + "{\"addHeaders\":{\"extproc\":\"true\"}}" + ], + "User-Agent": [ + "curl/8.7.1" + ] + } + } + ... + ``` + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} extproc -n {{< reuse "agw-docs/snippets/namespace.md" >}} +kubectl delete deployment ext-proc-grpc -n {{< reuse "agw-docs/snippets/namespace.md" >}} +kubectl delete service ext-proc-grpc -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` diff --git a/content/docs/kubernetes/1.1.x/traffic-management/grpc.md b/content/docs/kubernetes/1.1.x/traffic-management/grpc.md new file mode 100644 index 000000000..74f38918c --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/grpc.md @@ -0,0 +1,13 @@ +--- +title: gRPC routing +weight: 10 +description: +test: + grpc: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/traffic-management/grpc.md + path: grpc +--- + +{{< reuse "agw-docs/pages/traffic-management/grpc.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/traffic-management/header-control/_index.md b/content/docs/kubernetes/1.1.x/traffic-management/header-control/_index.md new file mode 100644 index 000000000..09342c555 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/header-control/_index.md @@ -0,0 +1,9 @@ +--- +title: Header control +weight: 10 +test: skip +--- + +Modify the headers of HTTP requests and responses. + + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/header-control/early-request-header-modifier.md b/content/docs/kubernetes/1.1.x/traffic-management/header-control/early-request-header-modifier.md new file mode 100644 index 000000000..09bd18873 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/header-control/early-request-header-modifier.md @@ -0,0 +1,17 @@ +--- +title: Early request header modification +weight: 30 +test: + remove-reserved-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/header-control/early-request-header-modifier.md + path: remove-reserved-header +--- + +{{< reuse "agw-docs/pages/traffic-management/header-control/early-request-header-modifier.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/header-control/request-header.md b/content/docs/kubernetes/1.1.x/traffic-management/header-control/request-header.md new file mode 100644 index 000000000..1b0c9de6f --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/header-control/request-header.md @@ -0,0 +1,34 @@ +--- +title: Request headers +weight: 10 +test: + add-request-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/header-control/request-header.md + path: add-request-header + set-request-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/header-control/request-header.md + path: set-request-header + remove-request-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/header-control/request-header.md + path: remove-request-header +--- + +{{< reuse "agw-docs/pages/traffic-management/header-control/request-header.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/header-control/response-header.md b/content/docs/kubernetes/1.1.x/traffic-management/header-control/response-header.md new file mode 100644 index 000000000..ec1cc4742 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/header-control/response-header.md @@ -0,0 +1,34 @@ +--- +title: Response headers +weight: 20 +test: + add-response-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/header-control/response-header.md + path: add-response-header + set-response-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/header-control/response-header.md + path: set-response-header + remove-response-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/header-control/response-header.md + path: remove-response-header +--- + +{{< reuse "agw-docs/pages/traffic-management/header-control/response-header.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/match/_index.md b/content/docs/kubernetes/1.1.x/traffic-management/match/_index.md new file mode 100644 index 000000000..1c9f2a766 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/match/_index.md @@ -0,0 +1,8 @@ +--- +title: Matching +weight: 10 +test: skip +--- + +Match requests by header, host, method, path, or query parameters. + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/match/header.md b/content/docs/kubernetes/1.1.x/traffic-management/match/header.md new file mode 100644 index 000000000..d65a1c010 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/match/header.md @@ -0,0 +1,294 @@ +--- +title: Header +weight: 10 +test: + header-match-exact: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/header.md + path: header-match-exact + header-match-regex: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/header.md + path: header-match-regex +--- + +Specify a set of headers which incoming requests must match in entirety, such as with regular expressions (regex). + +For more information, see the [{{< reuse "agw-docs/snippets/k8s-gateway-api-name.md" >}} documentation](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches). + +{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} + +## Set up exact header matching + +Match headers by an exact string, such as `version`. + +1. Create an HTTPRoute resource. + ```yaml {paths="header-match-exact"} + kubectl apply -f- <}} + hostnames: + - match.example + rules: + - matches: + - headers: + - name: version + value: v2 + type: Exact + backendRefs: + - name: httpbin + port: 8000 + EOF + ``` + +2. Send a request to the httpbin app on the `match.example` domain without any headers. Verify that you get back a 404 HTTP response code as no matching request could be found. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 \ + -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 \ + -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 404 Not Found + HTTP/1.1 404 Not Found + < content-length: 9 + content-length: 9 + < content-type: text/plain; charset=utf-8 + content-type: text/plain; charset=utf-8 + ``` + +3. Send another request to the httpbin app on the `match.example` domain. This time, add the `version: v2` header that you configured in the HTTPRoute. Verify that your request now succeeds and you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 \ + -H "host: match.example" \ + -H "version: v2" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 \ + -H "host: match.example" \ + -H "version: v2" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + * Request completely sent off + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-length: 0 + content-length: 0 + ``` + +## Set up regex header matching + +Match headers with regular expressions (regex). + +1. Create an HTTPRoute resource to match multiple headers with regex. Only if all headers are present in the request, the request is accepted and processed by the gateway proxy. The following rules apply: + * ` (dogs|cats)`: The value of the `pet` request header must either be `dogs` or `cats`. + * `\\d[.]\\d.*`: The value of the `version` header must meet the following conditions: + * `\\d` matches a single digit. + * `[.]` matches a literal period. + * `\\d.*` matches a single digit followed by zero or any character. + * Allowed pattern: `3.0-game`, not allowed: `30` + * `Bearer\s.*`: The value of the `Authorization` request header must be `Bearer` followed by a space (`\s`), followed by zero or any characters (`.*`). + * Allowed pattern: `Bearer 123`, not allowed: `Bearer` + ```yaml {paths="header-match-regex"} + kubectl apply -f- <}} + hostnames: + - match.example + rules: + - matches: + - headers: + - name: pet + value: (dogs|cats) + type: RegularExpression + - name: version + value: \\d[.]\\d.* + type: RegularExpression + - name: Authorization + value: Bearer\s.* + type: RegularExpression + backendRefs: + - name: httpbin + port: 8000 + EOF + ``` + +2. Send a request to the httpbin app on the `match.example` domain and add valid values for each of your headers. Verify that the request succeeds and you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 -H "host: match.example" -H "host: match.example" \ + -H "Authorization: Bearer 123" \ + -H "pet: dogs" \ + -H "version: 3.0" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 -H "host: match.example" -H "host: match.example" \ + -H "Authorization: Bearer 123" \ + -H "pet: dogs" \ + -H "version: 3.0" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + * Request completely sent off + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-length: 0 + content-length: 0 + ``` + +3. Send another request to the httpbin app on the `match.example` domain. This time, you change the value of the `version` header to an invalid value that does not meet the regular expression that you defined. Verify that the request is denied with a 404 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 -H "host: match.example" -H "host: match.example" \ + -H "Authorization: Bearer 123" \ + -H "pet: dogs" \ + -H "version: 30" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 -H "host: match.example" -H "host: match.example" \ + -H "Authorization: Bearer 123" \ + -H "pet: dogs" \ + -H "version: 30" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 404 Not Found + HTTP/1.1 404 Not Found + < content-length: 9 + content-length: 9 + < content-type: text/plain; charset=utf-8 + content-type: text/plain; charset=utf-8 + ``` +{{< doc-test paths="header-match-exact" >}} +YAMLTest -f - <<'EOF' +- name: exact header match - no version header returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +- name: exact header match - version v2 header returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: match.example + version: v2 + source: + type: local + expect: + statusCode: 200 +EOF +{{< /doc-test >}} + +{{< doc-test paths="header-match-regex" >}} +YAMLTest -f - <<'EOF' +- name: regex header match - valid headers returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: match.example + Authorization: "Bearer 123" + pet: dogs + version: "3.0" + source: + type: local + expect: + statusCode: 200 +- name: regex header match - invalid version returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: match.example + Authorization: "Bearer 123" + pet: dogs + version: "30" + source: + type: local + expect: + statusCode: 404 +EOF +{{< /doc-test >}} + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete httproute httpbin-match -n httpbin +``` + + + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/match/host.md b/content/docs/kubernetes/1.1.x/traffic-management/match/host.md new file mode 100644 index 000000000..e08cb6239 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/match/host.md @@ -0,0 +1,135 @@ +--- +title: Host +weight: 10 +test: + host-match: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/host.md + path: host-match +--- +Expose a route on multiple hosts. + +For more information, see the [{{< reuse "agw-docs/snippets/k8s-gateway-api-name.md" >}} documentation](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches). + +{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} + +## Set up host matching + +1. Create an HTTPRoute that is exposed on two domains, `host1.example` and `host2.example`. + ```yaml {paths="host-match"} + kubectl apply -f- <}} + hostnames: + - host1.example + - host2.example + rules: + - backendRefs: + - name: httpbin + port: 8000 + EOF + ``` + +2. Send a request to the `/status/200` path of the httpbin app on the `host1.example` domain. Verify that you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 -H "host: host1.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 -H "host: host1.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + * Request completely sent off + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-length: 0 + content-length: 0 + ``` + +3. Send another request to the httpbin app. This time, you send it along the `host2.example` domain. Verify that the request succeeds and that you also get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 -H "host: host2.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 -H "host: host2.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + * Request completely sent off + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-length: 0 + content-length: 0 + ``` + +{{< doc-test paths="host-match" >}} +YAMLTest -f - <<'EOF' +- name: host match - host1.example returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: host1.example + source: + type: local + expect: + statusCode: 200 +- name: host match - host2.example returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: host2.example + source: + type: local + expect: + statusCode: 200 +EOF +{{< /doc-test >}} + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete httproute httpbin-match -n httpbin +``` + + + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/match/method.md b/content/docs/kubernetes/1.1.x/traffic-management/match/method.md new file mode 100644 index 000000000..712c4dd6c --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/match/method.md @@ -0,0 +1,155 @@ +--- +title: HTTP method +weight: 10 +test: + method-match: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/method.md + path: method-match +--- + +Specify an HTTP method, such as POST, GET, PUT, PATCH, or DELETE, to match requests against. + +For more information, see the [{{< reuse "agw-docs/snippets/k8s-gateway-api-name.md" >}} documentation](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches). + +{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} + +## Set up HTTP method matching + +1. Create an HTTPRoute resource for the `match.example` domain that serves incoming GET requests for the httpbin app. + ```yaml {paths="method-match"} + kubectl apply -f- <}} + hostnames: + - match.example + rules: + - matches: + - method: "GET" + backendRefs: + - name: httpbin + port: 8000 + EOF + ``` + +2. Send a GET request to the httpbin app on the `match.example` domain. Verify that you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/get -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/get -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-type: application/json; encoding=utf-8 + content-type: application/json; encoding=utf-8 + < content-length: 322 + content-length: 322 + < + + { + "args": {}, + "headers": { + "Accept": [ + "*/*" + ], + "Host": [ + "match.example" + ], + "Traceparent": [ + "00-30f560cded924883c1eebfecfd8a8367-3eedcde5b0a6924f-01" + ], + "User-Agent": [ + "curl/8.7.1" + ] + }, + "origin": "10.xxx.x.xx:55304", + "url": "http://match.example/get" + } + ``` + +3. Send another request to the httpbin app on the `match.example` domain. This time, you use the `POST` method. Verify that your request is not forwarded to the httpbin app and that you get back a 404 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi -X POST http://$INGRESS_GW_ADDRESS:80/post -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi -X POST localhost:8080/post -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 404 Not Found + HTTP/1.1 404 Not Found + < content-length: 9 + content-length: 9 + < content-type: text/plain; charset=utf-8 + content-type: text/plain; charset=utf-8 + ``` + +{{< doc-test paths="method-match" >}} +YAMLTest -f - <<'EOF' +- name: method match - GET request returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /get + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 200 +- name: method match - POST request returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /post + method: POST + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +EOF +{{< /doc-test >}} + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete httproute httpbin-match -n httpbin --ignore-not-found +``` + + + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/match/path.md b/content/docs/kubernetes/1.1.x/traffic-management/match/path.md new file mode 100644 index 000000000..d7470f801 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/match/path.md @@ -0,0 +1,596 @@ +--- +title: Path +weight: 10 +test: + path-match-exact: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/path.md + path: path-match-exact + path-match-prefix: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/path.md + path: path-match-prefix + path-match-regex: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/path.md + path: path-match-regex +--- + +Match the targeted path of an incoming request against specific path criteria. + +For more information, see the [{{< reuse "agw-docs/snippets/k8s-gateway-api-name.md" >}} documentation](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches). + +{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} + +## Set up exact matching + +1. Create an HTTPRoute resource for the `match.example` domain that matches incoming requests on the `/status/200` exact path. + ```yaml {paths="path-match-exact"} + kubectl apply -f- <}} + hostnames: + - match.example + rules: + - matches: + - path: + type: Exact + value: /status/200 + backendRefs: + - name: httpbin + port: 8000 + EOF + ``` + +2. Send a request to the `/status/200` path of the httpbin app on the `match.example` domain. Verify that you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + * Request completely sent off + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-length: 0 + content-length: 0 + ``` + +3. Send another request to the httpbin app. This time, use the `/headers` path. Because this path is not specified in the HTTPRoute, the request fails and a 404 HTTP response code is returned. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/headers -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/headers -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 404 Not Found + HTTP/1.1 404 Not Found + < content-length: 9 + content-length: 9 + < content-type: text/plain; charset=utf-8 + content-type: text/plain; charset=utf-8 + ``` + +{{< doc-test paths="path-match-exact" >}} +for i in $(seq 1 100); do + curl -s --max-time 5 -o /dev/null "http://${INGRESS_GW_ADDRESS}:80/get" -H "host: match.example" && break + sleep 2 +done +YAMLTest -f - <<'EOF' +- name: wait for httpbin-match HTTPRoute to be accepted + wait: + target: + kind: HTTPRoute + metadata: + namespace: httpbin + name: httpbin-match + jsonPath: "$.status.parents[0].conditions[?(@.type=='Accepted')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +- name: wait for httpbin-match HTTPRoute refs to be resolved + wait: + target: + kind: HTTPRoute + metadata: + namespace: httpbin + name: httpbin-match + jsonPath: "$.status.parents[0].conditions[?(@.type=='ResolvedRefs')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +- name: exact path match - /status/200 returns 200 + retries: 1 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 200 +- name: exact path match - /headers returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /headers + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +EOF +{{< /doc-test >}} + +## Set up prefix path matching + +1. Create an HTTPRoute resource for the `match.example` domain that matches incoming requests on the `/anything` prefix path. + ```yaml {paths="path-match-prefix"} + kubectl apply -f- <}} + hostnames: + - match.example + rules: + - matches: + - path: + type: PathPrefix + value: /anything + backendRefs: + - name: httpbin + port: 8000 + EOF + ``` + +2. Send a request to the `/anything/team1` path of the httpbin app on the `match.example` domain. Verify that you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/anything/team1 -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/anything/team1 -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-type: application/json; encoding=utf-8 + content-type: application/json; encoding=utf-8 + < content-length: 304 + content-length: 304 + < + + { + "args": {}, + "headers": { + "Accept": [ + "*/*" + ], + "Host": [ + "match.example" + ], + "User-Agent": [ + "curl/8.7.1" + ] + }, + "origin": "10.xxx.x.xx:35204", + "url": "http://match.example/anything/team1", + "data": "", + "files": null, + "form": null, + "json": null + } + ``` + +3. Send another request to the httpbin app. This time, use the `/headers` path. Because this path is not specified in the HTTPRoute, the request fails and a 404 HTTP response code is returned. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/headers -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/headers -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 404 Not Found + HTTP/1.1 404 Not Found + < content-length: 9 + content-length: 9 + < content-type: text/plain; charset=utf-8 + content-type: text/plain; charset=utf-8 + ``` + +{{< doc-test paths="path-match-prefix" >}} +for i in $(seq 1 100); do + curl -s --max-time 5 -o /dev/null "http://${INGRESS_GW_ADDRESS}:80/get" -H "host: match.example" && break + sleep 2 +done +YAMLTest -f - <<'EOF' +- name: wait for httpbin-match HTTPRoute to be accepted + wait: + target: + kind: HTTPRoute + metadata: + namespace: httpbin + name: httpbin-match + jsonPath: "$.status.parents[0].conditions[?(@.type=='Accepted')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +- name: wait for httpbin-match HTTPRoute refs to be resolved + wait: + target: + kind: HTTPRoute + metadata: + namespace: httpbin + name: httpbin-match + jsonPath: "$.status.parents[0].conditions[?(@.type=='ResolvedRefs')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +- name: prefix path match - /anything/team1 returns 200 + retries: 1 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /anything/team1 + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 200 +- name: prefix path match - /headers returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /headers + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +EOF +{{< /doc-test >}} + +## Set up regex matching + +Use [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions to match incoming requests. + +1. Create an HTTPRoute resource for the `match.example` domain that uses a regular expression (regex) to match incoming requests. The following regex patterns are defined in the example: + * **`/.*my-path.*`**: + * The request path must start with `/`. + * The expression `.*` means that any character before and after the `my-path` string is allowed. + * Allowed pattern: `/anything/this-is-my-path-1`, not allowed: `/anything`. + * **`/anything/stores/[^/]+?/entities`**: + * The request path must start with `/anything/stores/`. + * `[^/]+?` matches any character except `/`. + * The request path must end with `/entities`. + * Allowed pattern: `/anything/stores/us/entities`, not allowed: `/anything/stores/us/south/entities`. + * **`/anything/(dogs|cats)/\\d[.]\\d.*`** + * The request path must start with `/anything/`, followed by either `dogs/` or `cats/`. + * `\\d` matches a single digit. + * `[.]` matches a literal period. + * `\\d.*` matches a single digit followed by zero or any character. + * Allowed pattern: `/anything/dogs/3.0-game`, not allowed: `/anything/birds` + ```yaml {paths="path-match-regex"} + kubectl apply -f- <}} + hostnames: + - "match.example" + rules: + - matches: + - path: + type: RegularExpression + value: /.*my-path.* + - path: + type: RegularExpression + value: /anything/stores/[^/]+?/entities + - path: + type: RegularExpression + value: /anything/(dogs|cats)/\\d[.]\\d.* + backendRefs: + - name: httpbin + namespace: httpbin + port: 8000 + EOF + ``` + +2. Send multiple requests to the httpbin app on the `match.example` domain. + * `/anything/this-is-my-path-1` matches the regex pattern `\/.*my-path.*` + * `/anything/stores/us/entities` matches the regex pattern `/anything/stores/[^/]+?/entities` + * `/anything/dogs/3.0-game` matches the regex pattern `/anything/(dogs|cats)/\\d[.]\\d.*` + + Verify that the requests succeed and that you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/anything/this-is-my-path-1 -H "host: match.example" + curl -vi http://$INGRESS_GW_ADDRESS:80/anything/stores/us/entities -H "host: match.example" + curl -vi http://$INGRESS_GW_ADDRESS:80/anything/dogs/3.0-game -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/anything/this-is-my-path-1 -H "host: match.example" + curl -vi localhost:8080/anything/stores/us/entities -H "host: match.example" + curl -vi localhost:8080/anything/dogs/3.0-game -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-type: application/json; encoding=utf-8 + content-type: application/json; encoding=utf-8 + < content-length: 317 + content-length: 317 + < + + { + "args": {}, + "headers": { + "Accept": [ + "*/*" + ], + "Host": [ + "match.example" + ], + "User-Agent": [ + "curl/8.7.1" + ] + }, + "origin": "10.xxx.x.xx:43182", + "url": "http://match.example/anything/stores/us/entities", + "data": "", + "files": null, + "form": null, + "json": null + } + ``` + +3. Send requests to the httpbin app that do not meet the defined regex patterns. + * `/anything` does not match the regex pattern `\/.*my-path.*` + * `/anything/stores/us/south/entities` does not match the regex pattern `/anything/stores/[^/]+?/ + * `/anything/birds/1.1-game` does not match the regex pattern `/anything/(dogs|cats)/\\d[.]\\d.*` + + Verify that all requests fail with a 404 HTTP response code, because the path does not match the regex pattern that you defined. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/anything -H "host: match.example" + curl -vi http://$INGRESS_GW_ADDRESS:80/anything/stores/us/south/entities -H "host: match.example" + curl -vi http://$INGRESS_GW_ADDRESS:80/anything/birds/1.1-game -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/anything -H "host: match.example" + curl -vi localhost:8080/anything/stores/us/south/entities -H "host: match.example" + curl -vi localhost:8080/anything/birds/1.1-game -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 404 Not Found + HTTP/1.1 404 Not Found + < content-length: 9 + content-length: 9 + < content-type: text/plain; charset=utf-8 + content-type: text/plain; charset=utf-8 + ``` + + +{{< doc-test paths="path-match-regex" >}} +for i in $(seq 1 100); do + curl -s --max-time 5 -o /dev/null "http://${INGRESS_GW_ADDRESS}:80/get" -H "host: match.example" && break + sleep 2 +done +YAMLTest -f - <<'EOF' +- name: wait for httpbin-match HTTPRoute to be accepted + wait: + target: + kind: HTTPRoute + metadata: + namespace: httpbin + name: httpbin-match + jsonPath: "$.status.parents[0].conditions[?(@.type=='Accepted')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +- name: wait for httpbin-match HTTPRoute refs to be resolved + wait: + target: + kind: HTTPRoute + metadata: + namespace: httpbin + name: httpbin-match + jsonPath: "$.status.parents[0].conditions[?(@.type=='ResolvedRefs')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +- name: regex path match - /anything/this-is-my-path-1 returns 200 + retries: 1 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /anything/this-is-my-path-1 + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 200 +- name: regex path match - /anything/stores/us/entities returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /anything/stores/us/entities + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 200 +- name: regex path match - /anything/dogs/3.0-game returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /anything/dogs/3.0-game + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 200 +- name: regex path match - /anything returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /anything + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +- name: regex path match - /anything/stores/us/south/entities returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /anything/stores/us/south/entities + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +- name: regex path match - /anything/birds/1.1-game returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /anything/birds/1.1-game + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +EOF +{{< /doc-test >}} + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete httproute httpbin-match -n httpbin --ignore-not-found +``` + + + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/match/query.md b/content/docs/kubernetes/1.1.x/traffic-management/match/query.md new file mode 100644 index 000000000..c77d3fba3 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/match/query.md @@ -0,0 +1,138 @@ +--- +title: Query parameter +weight: 10 +description: Specify a set of URL query parameters which requests must match in entirety. +test: + query-match: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/match/query.md + path: query-match +--- + +Specify a set of URL query parameters which requests must match in entirety. + +For more information, see the [{{< reuse "agw-docs/snippets/k8s-gateway-api-name.md" >}} documentation](https://gateway-api.sigs.k8s.io/api-types/httproute/#matches). + +{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}} + +## Set up query parameter matching + +1. Create an HTTPRoute resource for the `match.example` domain that matches incoming requests with a `user=me` query parameter. + ```yaml {paths="query-match"} + kubectl apply -f- <}} + hostnames: + - match.example + rules: + - matches: + - queryParams: + - type: Exact + value: me + name: user + backendRefs: + - name: httpbin + port: 8000 + EOF + ``` + +2. Send a request to the `/status/200` path of the httpbin app on the `match.example` domain without any query parameters. Verify that your request is not forwarded to the httpbin app because no matching query parameter is found. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi http://$INGRESS_GW_ADDRESS:80/status/200 -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi localhost:8080/status/200 -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + < HTTP/1.1 404 Not Found + HTTP/1.1 404 Not Found + < content-length: 9 + content-length: 9 + < content-type: text/plain; charset=utf-8 + content-type: text/plain; charset=utf-8 + ``` + +3. Send a request to the `/status/200` path of the httpbin app on the `match.example` domain. This time, you provide the `user=me` query parameter. Verify that your request now succeeds and that you get back a 200 HTTP response code. + {{< tabs items="Cloud Provider LoadBalancer,Port-forward for local testing" tabTotal="2" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -vi "http://$INGRESS_GW_ADDRESS:80/status/200?user=me" -H "host: match.example" + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -vi "localhost:8080/status/200?user=me" -H "host: match.example" + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ``` + * Request completely sent off + < HTTP/1.1 200 OK + HTTP/1.1 200 OK + < access-control-allow-credentials: true + access-control-allow-credentials: true + < access-control-allow-origin: * + access-control-allow-origin: * + < content-length: 0 + content-length: 0 + ``` + +{{< doc-test paths="query-match" >}} +YAMLTest -f - <<'EOF' +- name: query match - no query param returns 404 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200 + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 404 +- name: query match - user=me query param returns 200 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /status/200?user=me + method: GET + headers: + host: match.example + source: + type: local + expect: + statusCode: 200 +EOF +{{< /doc-test >}} + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete httproute httpbin-match -n httpbin --ignore-not-found +``` + + + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/redirect/_index.md b/content/docs/kubernetes/1.1.x/traffic-management/redirect/_index.md new file mode 100644 index 000000000..9b80088ed --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/redirect/_index.md @@ -0,0 +1,8 @@ +--- +title: Redirects +weight: 10 +test: skip +--- + +Redirect requests to another host or path. + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/redirect/host.md b/content/docs/kubernetes/1.1.x/traffic-management/redirect/host.md new file mode 100644 index 000000000..ea2309d91 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/redirect/host.md @@ -0,0 +1,18 @@ +--- +title: Host redirect +weight: 442 +description: Redirect requests to a different host. +test: + host-redirect: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/redirect/host.md + path: host-redirect +--- + +{{< reuse "agw-docs/pages/traffic-management/redirect/host.md" >}} + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/redirect/https.md b/content/docs/kubernetes/1.1.x/traffic-management/redirect/https.md new file mode 100644 index 000000000..ce1dfab16 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/redirect/https.md @@ -0,0 +1,17 @@ +--- +title: HTTPS redirect +weight: 441 +description: Redirect HTTP traffic to HTTPS. +test: + https-redirect: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/redirect/https.md + path: https-redirect +--- + +{{< reuse "agw-docs/pages/traffic-management/redirect/https.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/traffic-management/redirect/path.md b/content/docs/kubernetes/1.1.x/traffic-management/redirect/path.md new file mode 100644 index 000000000..3183f261e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/redirect/path.md @@ -0,0 +1,26 @@ +--- +title: Path redirects +weight: 443 +description: Redirect requests to a different path prefix. +test: + path-redirect-prefix: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/redirect/path.md + path: path-redirect-prefix + path-redirect-full: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/redirect/path.md + path: path-redirect-full +--- + +{{< reuse "agw-docs/pages/traffic-management/redirect/path.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/1.1.x/traffic-management/rewrite/_index.md b/content/docs/kubernetes/1.1.x/traffic-management/rewrite/_index.md new file mode 100644 index 000000000..851355b2b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/rewrite/_index.md @@ -0,0 +1,8 @@ +--- +title: Rewrites +weight: 10 +test: skip +--- + +Change the host header or prefix path in requests. + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/rewrite/host.md b/content/docs/kubernetes/1.1.x/traffic-management/rewrite/host.md new file mode 100644 index 000000000..6ecaa4dd0 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/rewrite/host.md @@ -0,0 +1,17 @@ +--- +title: Host rewrites +weight: 461 +description: Replace the host header value before forwarding a request to a backend service. +test: + host-rewrite: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/rewrite/host.md + path: host-rewrite +--- + +{{< reuse "agw-docs/pages/traffic-management/rewrite/host.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/rewrite/path.md b/content/docs/kubernetes/1.1.x/traffic-management/rewrite/path.md new file mode 100644 index 000000000..d7d922b92 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/rewrite/path.md @@ -0,0 +1,26 @@ +--- +title: Path rewrites +weight: 462 +description: Rewrite path prefixes in requests. +test: + path-rewrite-prefix: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/rewrite/path.md + path: path-rewrite-prefix + path-rewrite-full: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/rewrite/path.md + path: path-rewrite-full +--- + +{{< reuse "agw-docs/pages/traffic-management/rewrite/path.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/traffic-split.md b/content/docs/kubernetes/1.1.x/traffic-management/traffic-split.md new file mode 100644 index 000000000..de262b986 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/traffic-split.md @@ -0,0 +1,17 @@ +--- +title: Traffic splitting +weight: 60 +description: Set up A/B testing, traffic splitting, and canary deployments using weighted routing. +test: + traffic-split-llm-models: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/traffic-management/traffic-split.md + path: traffic-split-llm +--- + +{{< reuse "agw-docs/pages/traffic-management/traffic-split.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations.txt b/content/docs/kubernetes/1.1.x/traffic-management/transformations.txt new file mode 100644 index 000000000..3bb3dc2e6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations.txt @@ -0,0 +1,108 @@ +--- +title: Transformations +weight: 10 +description: Use CEL expressions to transform requests and responses to LLMs, MCP servers, and agents. +--- + +## About CEL expressions + +CEL (Common Expression Language) transformations allows you to use dynamic expressions to extract and transform values from requests and responses. + +For an overview of supported CEL expressions, see the [agentgateway docs](https://agentgateway.dev/docs/standalone/latest/reference/cel/). + +## About this guide + +This guide walks you through how to set up CEL-based transformations for the OpenAI LLM provider. Note that you can use the same {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource to apply transformations to MCP server, inference, or agent routes. + +## Before you begin + +{{< reuse "agw-docs/snippets/agw-prereq-llm.md" >}} + +{{< callout type="info" >}} +Note that this guide assumes that you want to apply the policy to the OpenAI LLM provider. You can use other LLM providers or apply external auth to an MCP server or agent. Make sure to adjust these steps to apply to your {{< reuse "agw-docs/snippets/backend.md" >}} type. +{{< /callout >}} + +## Set up transformations + +1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} with your transformation rules. In this example, you use a CEL expression to extract the path from the request. The request path is then added to the `response-gateway` header. + ```yaml + kubectl apply -f- <}} + kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} + metadata: + name: transformation + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: agentgateway-proxy + traffic: + transformation: + response: + set: + - name: response-gateway + value: "'response path ' + request.path" + EOF + ``` + +2. Send a request to the OpenAI API. Verify that the request succeeds and that you see the `response-gateway` header with a value of `response path /openai`. + + {{< tabs tabTotal= "2" items="Cloud Provider LoadBalancer,Port-forward for local testing" >}} + {{% tab tabName="Cloud Provider LoadBalancer" %}} + ```sh + curl -v "${INGRESS_GW_ADDRESS}:8080/openai" -H content-type:application/json -H "X-User-ID: user123" -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "system", + "content": "You are a poetic assistant, skilled in explaining complex programming concepts with creative flair." + }, + { + "role": "user", + "content": "In the style of Shakespeare, write a series of sonnets that explain the concept of recursion in programming." + } + ] + }' + ``` + {{% /tab %}} + {{% tab tabName="Port-forward for local testing" %}} + ```sh + curl -v "localhost:8080/openai" -H content-type:application/json -H "X-User-ID: user123" -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "system", + "content": "You are a poetic assistant, skilled in explaining complex programming concepts with creative flair." + }, + { + "role": "user", + "content": "In the style of Shakespeare, write a series of sonnets that explain the concept of recursion in programming." + } + ] + }' + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + ```console {hl_lines=[9]} + * upload completely sent off: 358 bytes + < HTTP/1.1 200 OK + < content-type: application/json + < access-control-expose-headers: X-Request-ID + < openai-organization: solo-io-1 + < openai-processing-ms: 1082 + < openai-version: 2020-10-01 + ... + < response-gateway: response path /openai + < content-length: 1198 + ``` + +## Cleanup + +{{< reuse "agw-docs/snippets/cleanup.md" >}} + +```sh +kubectl delete {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} transformation -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/_index.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/_index.md new file mode 100644 index 000000000..7e5e68d8e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/_index.md @@ -0,0 +1,8 @@ +--- +title: Transformations +weight: 10 +test: skip +--- + +Mutate and transform requests and responses before forwarding them to the destination. + diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/access-logs.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/access-logs.md new file mode 100644 index 000000000..017c60244 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/access-logs.md @@ -0,0 +1,26 @@ +--- +title: Enrich access logs +weight: 120 +description: Log CEL context variables to access logs to inspect and debug transformation expressions at runtime. +test: + access-logs: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/access-logs.md + path: access-logs + access-logs-filter: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/access-logs.md + path: access-logs-filter +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/access-logs.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/encode.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/encode.md new file mode 100644 index 000000000..99868294e --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/encode.md @@ -0,0 +1,25 @@ +--- +title: Encode base64 headers +weight: 20 +description: Automatically encode and decode base64 values in request headers. +test: + encode: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/encode.md + path: encode + decode: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/encode.md + path: decode +--- +{{< reuse "agw-docs/pages/traffic-management/transformations/encode.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/filter-request-body.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/filter-request-body.md new file mode 100644 index 000000000..d08f8ac0f --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/filter-request-body.md @@ -0,0 +1,17 @@ +--- +title: Filter and merge request body fields +weight: 60 +description: Use filterKeys() and merge() CEL functions to strip unwanted fields from a JSON request body and inject defaults before forwarding to the upstream. +test: + filter-request-body: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/filter-request-body.md + path: filter-request-body +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/filter-request-body.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/forward.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/forward.md new file mode 100644 index 000000000..e900189c4 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/forward.md @@ -0,0 +1,17 @@ +--- +title: Forward request URLs +weight: 40 +description: Use CEL expressions to construct a full request URL from context variables and forward it upstream as a request header. +test: + forward: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/forward.md + path: forward +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/forward.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/inject-response-body.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/inject-response-body.md new file mode 100644 index 000000000..4207b5ac7 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/inject-response-body.md @@ -0,0 +1,26 @@ +--- +title: Inject response bodies +weight: 55 +description: Learn how to return a customized response body and how to replace specific values in the body. +test: + inject-header-into-body: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/inject-response-body.md + path: inject-header-into-body + inject-body-field-into-body: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/inject-response-body.md + path: inject-body-field-into-body +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/inject-response-body.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/inject-response-headers.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/inject-response-headers.md new file mode 100644 index 000000000..c92d0efda --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/inject-response-headers.md @@ -0,0 +1,17 @@ +--- +title: Inject response headers +weight: 5 +description: Extract values from a request header and inject it as a header to your response. +test: + inject-response-headers: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/inject-response-headers.md + path: inject-response-headers +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/inject-response-headers.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/llm-model-headers.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/llm-model-headers.md new file mode 100644 index 000000000..2594655d1 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/llm-model-headers.md @@ -0,0 +1,28 @@ +--- +title: Inject LLM model headers +weight: 47 +description: Detect model fallback by injecting the requested and actual LLM model names as response headers using llm.requestModel and llm.responseModel CEL variables. +test: + llm-transformations: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/traffic-management/transformations/llm-model-headers.md + path: llm-transformations + + llm-model-headers: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: standard + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/llm/providers/openai.md + path: openai-setup + - file: content/docs/kubernetes/latest/traffic-management/transformations/llm-model-headers.md + path: llm-model-headers + +--- + +{{< reuse "agw-docs/pages/agentgateway/llm/transformations.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/path-method.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/path-method.md new file mode 100644 index 000000000..0babe1fa2 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/path-method.md @@ -0,0 +1,17 @@ +--- +title: Change request path and method +weight: 45 +description: Use pseudo headers to conditionally rewrite the request path and HTTP method based on a request header value. +test: + path-method: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/path-method.md + path: path-method +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/path-method.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/query.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/query.md new file mode 100644 index 000000000..622d1abdb --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/query.md @@ -0,0 +1,17 @@ +--- +title: Promote query parameters to request headers +weight: 46 +description: Read a query parameter from the request URI and inject it as a request header using a CEL conditional expression. +test: + query: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/query.md + path: query +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/query.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/remove-header.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/remove-header.md new file mode 100644 index 000000000..78cd0b123 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/remove-header.md @@ -0,0 +1,17 @@ +--- +title: Remove headers +weight: 50 +description: Remove sensitive or internal headers from requests before they reach the upstream. +test: + remove-header: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/remove-header.md + path: remove-header +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/remove-header.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/rewrite.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/rewrite.md new file mode 100644 index 000000000..3fd58189f --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/rewrite.md @@ -0,0 +1,17 @@ +--- +title: Rewrite dynamic path segments +weight: 30 +description: Use CEL functions to rewrite request paths in a header. +test: + rewrite: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/rewrite.md + path: rewrite +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/rewrite.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/status.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/status.md new file mode 100644 index 000000000..79b331593 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/status.md @@ -0,0 +1,17 @@ +--- +title: Change response bodies +weight: 60 +description: Update the response status based on the headers in a response. +test: + change-response-status: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/status.md + path: change-response-status +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/status.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/templating-language.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/templating-language.md new file mode 100644 index 000000000..151d7f0de --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/templating-language.md @@ -0,0 +1,7 @@ +--- +title: Templating language +weight: 1 +test: skip +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/templating-language.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/tracing.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/tracing.md new file mode 100644 index 000000000..5aa55025b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/tracing.md @@ -0,0 +1,17 @@ +--- +title: Generate request tracing headers +weight: 10 +description: Use uuid() and random() CEL functions to inject a unique request ID and a random sampling value into request headers. +test: + tracing: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/tracing.md + path: tracing +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/tracing.md" >}} diff --git a/content/docs/kubernetes/1.1.x/traffic-management/transformations/validate.md b/content/docs/kubernetes/1.1.x/traffic-management/transformations/validate.md new file mode 100644 index 000000000..ba7d9d112 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/traffic-management/transformations/validate.md @@ -0,0 +1,26 @@ +--- +title: Validate and set request body defaults +weight: 70 +description: Use default() and fail() CEL functions to enforce required fields and apply default values on a JSON request body. +test: + validate-defaults: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/validate.md + path: validate-defaults + validate-skip: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/install/sample-app.md + path: install-httpbin + - file: content/docs/kubernetes/latest/traffic-management/transformations/validate.md + path: validate-skip +--- + +{{< reuse "agw-docs/pages/traffic-management/transformations/validate.md" >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/_index.md b/content/docs/kubernetes/1.1.x/tutorials/_index.md new file mode 100644 index 000000000..968464cc5 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/_index.md @@ -0,0 +1,9 @@ +--- +title: Tutorials +weight: 23 +icon: school +description: Step-by-step guides for deploying and managing agentgateway on Kubernetes +test: skip +--- + +Learn how to deploy and manage agentgateway on Kubernetes through hands-on tutorials. Each tutorial includes setting up a local kind cluster so you can follow along from scratch. diff --git a/content/docs/kubernetes/1.1.x/tutorials/azure-ai-foundry/_index.md b/content/docs/kubernetes/1.1.x/tutorials/azure-ai-foundry/_index.md new file mode 100644 index 000000000..54dadfc89 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/azure-ai-foundry/_index.md @@ -0,0 +1,289 @@ +--- +title: Azure AI Foundry +weight: 8 +description: Route requests to Azure OpenAI through agentgateway on Kubernetes +--- + +Route LLM requests to Azure AI Foundry (Azure OpenAI) through agentgateway on Kubernetes, using custom path routing and URL rewriting. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster with agentgateway +2. Configure an Azure OpenAI backend with Azure AI Foundry credentials +3. Set up path-based routing with URL rewriting +4. Send chat completion requests through agentgateway to Azure OpenAI + +## Before you begin + +Make sure you have the following tools installed: +- [Docker](https://www.docker.com/products/docker-desktop/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kind](https://kind.sigs.k8s.io/) +- [Helm](https://helm.sh/docs/intro/install/) + +You also need: +- An Azure account with access to [Azure AI Foundry](https://ai.azure.com/) +- An Azure OpenAI deployment with an API key +- Your Azure OpenAI endpoint URL (e.g., `your-resource.services.ai.azure.com`) +- Your deployment name (e.g., `gpt-4o-mini`) + +For detailed tool installation instructions, see the [LLM Gateway tutorial](../llm-gateway/). + +--- + +## Step 1: Create a kind cluster + +```bash +kind create cluster --name agentgateway +``` + +--- + +## Step 2: Install agentgateway + +```bash +# Gateway API CRDs +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml + +# agentgateway CRDs +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} + +# Control plane +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +--- + +## Step 3: Create a Gateway + +```bash +kubectl apply -f- <}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the proxy: + +```bash +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 4: Set your Azure credentials + +Export your Azure AI Foundry API key: + +```bash +export AZURE_FOUNDRY_API_KEY= +``` + +--- + +## Step 5: Create the Kubernetes secret + +Store your Azure credentials in a Kubernetes secret: + +```bash +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $AZURE_FOUNDRY_API_KEY +EOF +``` + +--- + +## Step 6: Create the Azure OpenAI backend + +Create an {{< reuse "agw-docs/snippets/backend.md" >}} resource configured for Azure OpenAI. Replace `your-resource.services.ai.azure.com` with your actual Azure AI Foundry endpoint, and `gpt-4o-mini` with your deployment name. + +```bash +kubectl apply -f- <}} +metadata: + name: azureopenai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + ai: + provider: + azureopenai: + endpoint: your-resource.services.ai.azure.com + deploymentName: gpt-4o-mini + apiVersion: 2025-01-01-preview + policies: + auth: + secretRef: + name: azureopenai-secret +EOF +``` + +| Setting | Description | +|---------|-------------| +| `azureopenai.endpoint` | Your Azure AI Foundry resource endpoint | +| `azureopenai.deploymentName` | The name of your Azure OpenAI model deployment | +| `azureopenai.apiVersion` | The Azure OpenAI API version to use | +| `policies.auth.secretRef` | Reference to the secret containing your API key | + +Verify the backend was created: + +```bash +kubectl get agentgatewaybackend -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 7: Create the HTTPRoute with URL rewriting + +Create an HTTPRoute that routes requests on the `/azureopenai` path and rewrites the URL to the chat completions endpoint. + +```bash +kubectl apply -f- <}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - matches: + - path: + type: PathPrefix + value: /azureopenai + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v1/chat/completions + backendRefs: + - name: azureopenai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +This route: +- **Matches** requests to `/azureopenai` +- **Rewrites** the path to `/v1/chat/completions` before forwarding to Azure +- **Routes** to the Azure OpenAI backend + +--- + +## Step 8: Test the API + +Set up port-forwarding: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +Send a request to Azure OpenAI through agentgateway: + +```bash +curl "localhost:8080/azureopenai" \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant." + }, + { + "role": "user", + "content": "What is Azure AI Foundry in one sentence?" + } + ] + }' | jq +``` + +Example output: + +```json +{ + "id": "chatcmpl-...", + "object": "chat.completion", + "choices": [{ + "message": { + "role": "assistant", + "content": "Azure AI Foundry is Microsoft's unified platform for building, deploying, and managing AI applications and models at scale." + }, + "index": 0, + "finish_reason": "stop" + }] +} +``` + +--- + +## Multiple Azure deployments + +You can route to different Azure OpenAI deployments based on request paths. Create additional backends and routes for each deployment: + +```yaml +# Backend for a different model +apiVersion: agentgateway.dev/v1alpha1 +kind: AgentgatewayBackend +metadata: + name: azureopenai-gpt4 + namespace: agentgateway-system +spec: + ai: + provider: + azureopenai: + endpoint: your-resource.services.ai.azure.com + deploymentName: gpt-4 + apiVersion: 2025-01-01-preview + policies: + auth: + secretRef: + name: azureopenai-secret +``` + +Then add an HTTPRoute matching `/azureopenai-gpt4` to route to the new backend. + +--- + +## Cleanup + +```bash +kill %1 2>/dev/null +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/llm/providers/azure" title="Azure OpenAI Reference" subtitle="Complete Azure OpenAI configuration" >}} + {{< card path="/tutorials/llm-gateway" title="LLM Gateway" subtitle="Route to multiple LLM providers" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/basic/_index.md b/content/docs/kubernetes/1.1.x/tutorials/basic/_index.md new file mode 100644 index 000000000..49760fda6 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/basic/_index.md @@ -0,0 +1,409 @@ +--- +title: Basic MCP server +weight: 2 +description: Deploy and connect to an MCP server through agentgateway on Kubernetes +test: + basic-mcp: + - file: content/docs/kubernetes/latest/tutorials/basic/_index.md + path: basic-mcp +--- + +Deploy an MCP server on Kubernetes and connect to it through agentgateway using the Kubernetes Gateway API. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster using kind +2. Install the {{< reuse "agw-docs/snippets/kgateway.md" >}} control plane +3. Deploy an MCP server as a Kubernetes service +4. Create an {{< reuse "agw-docs/snippets/backend.md" >}} to connect to the MCP server +5. Route MCP traffic through agentgateway and test tool calls + +## Before you begin + +Make sure you have the following tools installed: +- [Docker](https://www.docker.com/products/docker-desktop/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kind](https://kind.sigs.k8s.io/) +- [Helm](https://helm.sh/docs/intro/install/) + +For detailed installation instructions for each tool, see the [LLM Gateway tutorial](../llm-gateway/). + +--- + +## Step 1: Create a kind cluster + +```bash +kind create cluster --name agentgateway +``` + +Verify the cluster is running: + +```bash +kubectl cluster-info --context kind-agentgateway +``` + +--- + +## Step 2: Install agentgateway + +Install the Gateway API CRDs, agentgateway CRDs, and the control plane. + +```bash {paths="basic-mcp"} +# Gateway API CRDs +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml + +# agentgateway CRDs +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} +# Control plane +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +Verify the control plane is running: + +```bash {paths="basic-mcp"} +kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 3: Create a Gateway + +```bash {paths="basic-mcp"} +kubectl apply -f- <}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the proxy to be ready: + +```bash {paths="basic-mcp"} +kubectl get gateway agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +```bash +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +{{< doc-test paths="basic-mcp" >}} +YAMLTest -f - <<'EOF' +- name: wait for agentgateway-proxy deployment to be ready + wait: + target: + kind: Deployment + metadata: + namespace: agentgateway-system + name: agentgateway-proxy + jsonPath: "$.status.availableReplicas" + jsonPathExpectation: + comparator: greaterThan + value: 0 + polling: + timeoutSeconds: 300 + intervalSeconds: 5 + +- name: wait for agentgateway-proxy service LB address + wait: + target: + kind: Service + metadata: + namespace: agentgateway-system + name: agentgateway-proxy + jsonPath: "$.status.loadBalancer.ingress[0].ip" + jsonPathExpectation: + comparator: exists + polling: + timeoutSeconds: 300 + intervalSeconds: 5 + setVars: + INGRESS_GW_ADDRESS: + value: true +EOF +{{< /doc-test >}} + +{{< doc-test paths="basic-mcp" >}} +export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-proxy -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") +{{< /doc-test >}} + +--- + +## Step 4: Deploy an MCP server + +Deploy the MCP "everything" server as a Kubernetes Deployment and Service. This server provides sample tools like `echo`, `add`, and `longRunningOperation`. + +```bash {paths="basic-mcp"} +kubectl apply -f- <}} +spec: + replicas: 1 + selector: + matchLabels: + app: mcp-server-everything + template: + metadata: + labels: + app: mcp-server-everything + spec: + containers: + - name: mcp-server + image: node:20-alpine + command: ["npx", "-y", "@modelcontextprotocol/server-everything", "streamableHttp"] + ports: + - containerPort: 3001 + readinessProbe: + tcpSocket: + port: 3001 + initialDelaySeconds: 10 + periodSeconds: 5 + failureThreshold: 30 +--- +apiVersion: v1 +kind: Service +metadata: + name: mcp-server-everything + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: mcp-server-everything +spec: + selector: + app: mcp-server-everything + ports: + - port: 3001 + targetPort: 3001 + appProtocol: agentgateway.dev/mcp +EOF +``` + +Wait for the MCP server pod to be ready. The first startup may take a minute as it downloads the npm packages. + +```bash +kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} -l app=mcp-server-everything -w +``` + +{{< doc-test paths="basic-mcp" >}} +YAMLTest -f - <<'EOF' +- name: wait for mcp-server-everything deployment to be ready + wait: + target: + kind: Deployment + metadata: + namespace: agentgateway-system + name: mcp-server-everything + jsonPath: "$.status.availableReplicas" + jsonPathExpectation: + comparator: greaterThan + value: 0 + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +EOF +{{< /doc-test >}} + +--- + +## Step 5: Create the MCP backend + +Create an {{< reuse "agw-docs/snippets/backend.md" >}} resource that connects to the MCP server using dynamic service discovery. + +```bash {paths="basic-mcp"} +kubectl apply -f- <}} +metadata: + name: mcp-backend + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + mcp: + targets: + - name: mcp-server-everything + selector: + services: + matchLabels: + app: mcp-server-everything +EOF +``` + +--- + +## Step 6: Create the HTTPRoute + +Route MCP traffic through the agentgateway proxy to the backend. + +```bash {paths="basic-mcp"} +kubectl apply -f- <}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: mcp-backend + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} + matches: + - path: + type: PathPrefix + value: /mcp +EOF +``` + +{{< doc-test paths="basic-mcp" >}} +YAMLTest -f - <<'EOF' +- name: wait for mcp-backend to be accepted + wait: + target: + kind: AgentgatewayBackend + metadata: + namespace: agentgateway-system + name: mcp-backend + jsonPath: "$.status.conditions[?(@.type=='Accepted')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 60 + intervalSeconds: 2 + +- name: wait for mcp HTTPRoute to be accepted + wait: + target: + kind: HTTPRoute + metadata: + namespace: agentgateway-system + name: mcp + jsonPath: "$.status.parents[0].conditions[?(@.type=='Accepted')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 120 + intervalSeconds: 2 +EOF +{{< /doc-test >}} + +{{< doc-test paths="basic-mcp" >}} +for i in $(seq 1 90); do + STATUS=$(curl -s --max-time 5 -o /dev/null -w "%{http_code}" -X POST "http://${INGRESS_GW_ADDRESS}:80/mcp" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' 2>/dev/null) + if [ "$STATUS" = "200" ]; then break; fi + sleep 2 +done +{{< /doc-test >}} + +{{< doc-test paths="basic-mcp" >}} +YAMLTest -f - <<'EOF' +- name: MCP endpoint accepts initialize request + retries: 10 + http: + url: "http://${INGRESS_GW_ADDRESS}:80" + path: /mcp + method: POST + headers: + content-type: application/json + accept: "application/json, text/event-stream" + body: | + {"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1} + source: + type: local + expect: + statusCode: 200 +EOF +{{< /doc-test >}} + +--- + +## Step 7: Test the MCP connection + +Set up port-forwarding: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +Initialize an MCP session: + +```bash +curl -s -i http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' +``` + +Copy the `mcp-session-id` from the response headers, then list available tools: + +```bash +curl -s http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -H "Mcp-Session-Id: YOUR_SESSION_ID" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":2}' +``` + +You should see tools like `echo`, `add`, `longRunningOperation`, and more. + +### Call a tool + +```bash +curl -s http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -H "Mcp-Session-Id: YOUR_SESSION_ID" \ + -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"echo","arguments":{"message":"Hello from Kubernetes!"}},"id":3}' +``` + +Example output: + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "content": [{"type": "text", "text": "Echo: Hello from Kubernetes!"}] + } +} +``` + +--- + +## Cleanup + +```bash +kill %1 2>/dev/null +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/tutorials/mcp-federation" title="MCP Federation" subtitle="Federate multiple MCP servers" >}} + {{< card path="/tutorials/jwt-authorization" title="JWT Authorization" subtitle="Secure with JWT authentication" >}} + {{< card path="/mcp/" title="MCP Documentation" subtitle="Complete MCP configuration reference" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/claude-code-proxy/_index.md b/content/docs/kubernetes/1.1.x/tutorials/claude-code-proxy/_index.md new file mode 100644 index 000000000..adf6c9542 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/claude-code-proxy/_index.md @@ -0,0 +1,449 @@ +--- +title: Claude Code CLI proxy +weight: 10 +description: Proxy and secure Claude Code CLI traffic through agentgateway on Kubernetes +--- + +Proxy Claude Code CLI traffic through agentgateway on Kubernetes to intercept, inspect, and secure agentic client requests before they reach Anthropic's API. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster with agentgateway and an Anthropic backend +2. Configure native Anthropic message routing (not OpenAI translation) +3. Connect Claude Code CLI to route all traffic through the gateway +4. Add prompt guards to block sensitive content in CLI prompts +5. Understand model selection considerations for Claude Code + +## Before you begin + +Make sure you have the following tools installed: +- [Docker](https://www.docker.com/products/docker-desktop/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kind](https://kind.sigs.k8s.io/) +- [Helm](https://helm.sh/docs/intro/install/) +- [Claude Code CLI](https://code.claude.com/docs) (`npm install -g @anthropic-ai/claude-code`) +- An Anthropic API key (get one at [platform.claude.com](https://platform.claude.com)) + +For detailed tool installation instructions, see the [LLM Gateway tutorial](../llm-gateway/). + +--- + +## Why proxy agentic CLI traffic? + +Architecture diagrams typically show agents running in production systems. But the majority of agentic traffic actually originates from developer laptops β€” tools like Claude Code CLI, Cursor, and Copilot. This traffic needs the same governance as production workloads. + +By routing Claude Code CLI through agentgateway, you get: + +- **Visibility** β€” See every prompt and response flowing through the gateway +- **Security** β€” Block sensitive data (PII, credentials) before it leaves the network +- **Governance** β€” Enforce organizational policies on developer AI usage +- **Auditability** β€” Log and trace all agentic interactions for compliance + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Claude Code β”‚ β”‚ agentgateway β”‚ β”‚ Anthropic API β”‚ +β”‚ CLI β”‚ ──── β”‚ + route policies β”‚ ──── β”‚ (Claude models) β”‚ +β”‚ (developer β”‚ β”‚ + prompt guards β”‚ β”‚ β”‚ +β”‚ laptop) β”‚ β”‚ + observability β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## Step 1: Create a kind cluster + +```bash +kind create cluster --name agentgateway +``` + +--- + +## Step 2: Install agentgateway + +```bash +# Gateway API CRDs +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml + +# agentgateway CRDs +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} + +# Control plane +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +Verify the control plane is running: + +```bash +kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 3: Create a Gateway + +```bash +kubectl apply -f- <}} + labels: + app: agentgateway +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the proxy: + +```bash +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 4: Create the Anthropic secret + +Export your Anthropic API key and create a Kubernetes secret: + +```bash +export ANTHROPIC_API_KEY= + +kubectl apply -f- <}} + labels: + app: agentgateway +type: Opaque +stringData: + Authorization: $ANTHROPIC_API_KEY +EOF +``` + +--- + +## Step 5: Create the Anthropic backend + +This is where the configuration differs from a standard LLM Gateway setup. Claude Code CLI sends requests to Anthropic's native `/v1/messages` endpoint β€” not the OpenAI-compatible `/v1/chat/completions` endpoint. You must configure the backend with explicit **route policies** so agentgateway handles the Anthropic message format correctly. + +{{< callout type="warning" >}} +**Model selection matters.** If you specify a model in the backend (e.g., `claude-sonnet-4-5-20250929`) but Claude Code CLI uses a different model, you may get a `400` error with a misleading message like "thinking mode isn't enabled." To avoid this, either match the model exactly or omit the model field to allow any model. +{{< /callout >}} + +{{< tabs items="Flexible model (recommended),Fixed model" >}} + +{{% tab tabName="Flexible model (recommended)" %}} + +This configuration allows Claude Code CLI to use any model. The `anthropic: {}` syntax means no model is pinned. + +```bash +kubectl apply -f- <}} +metadata: + name: anthropic + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + ai: + provider: + anthropic: {} + policies: + ai: + routes: + '/v1/messages': Messages + '*': Passthrough + auth: + secretRef: + name: anthropic-secret +EOF +``` +{{% /tab %}} + +{{% tab tabName="Fixed model" %}} + +This configuration pins the backend to a specific model. Make sure the model matches what Claude Code CLI is configured to use. + +```bash +kubectl apply -f- <}} +metadata: + name: anthropic + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + ai: + provider: + anthropic: + model: claude-sonnet-4-5-20250929 + policies: + ai: + routes: + '/v1/messages': Messages + '*': Passthrough + auth: + secretRef: + name: anthropic-secret +EOF +``` +{{% /tab %}} + +{{< /tabs >}} + +| Setting | Description | +|---------|-------------| +| `anthropic: {}` | Allow any model β€” Claude Code CLI sends the model in each request | +| `anthropic.model` | Pin to a specific model β€” must match the CLI's model selection | +| `routes['/v1/messages']` | Process requests in Anthropic's native message format (required for Claude Code) | +| `routes['*']` | Pass through all other requests (e.g., `/v1/models`) without LLM processing | + +--- + +## Step 6: Create the HTTPRoute + +Create an HTTPRoute that routes all traffic from the Gateway to the Anthropic backend: + +```bash +kubectl apply -f- <}} + labels: + app: agentgateway +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: anthropic + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +This route uses a `/` path prefix match so that all requests β€” including `/v1/messages`, `/v1/models`, and any other Claude Code CLI endpoints β€” are forwarded to the Anthropic backend. + +--- + +## Step 7: Test with Claude Code CLI + +Set up port-forwarding: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +### Single prompt test + +Send a quick test prompt through the gateway: + +```bash +ANTHROPIC_BASE_URL="http://localhost:8080" claude -p "What is Kubernetes?" +``` + +You should receive a normal response from Claude, confirming that traffic is flowing through agentgateway. + +### Interactive mode + +You can also start Claude Code CLI in interactive mode with all traffic routed through the gateway: + +```bash +ANTHROPIC_BASE_URL="http://localhost:8080" claude +``` + +This opens the full Claude Code CLI experience. Every request β€” prompts, tool calls, file reads β€” flows through agentgateway where it can be inspected, logged, and secured. + +--- + +## Step 8: Add prompt guards + +Now that connectivity is confirmed, add security by configuring prompt guards. This modifies the backend to reject requests containing specific patterns before they reach Anthropic. + +{{< tabs items="Flexible model,Fixed model" >}} + +{{% tab tabName="Flexible model" %}} +```bash +kubectl apply -f- <}} +metadata: + name: anthropic + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + ai: + provider: + anthropic: {} + policies: + ai: + routes: + '/v1/messages': Messages + '*': Passthrough + promptGuard: + request: + - response: + message: "Rejected due to inappropriate content" + regex: + action: Reject + matches: + - "credit card" + auth: + secretRef: + name: anthropic-secret +EOF +``` +{{% /tab %}} + +{{% tab tabName="Fixed model" %}} +```bash +kubectl apply -f- <}} +metadata: + name: anthropic + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + ai: + provider: + anthropic: + model: claude-sonnet-4-5-20250929 + policies: + ai: + routes: + '/v1/messages': Messages + '*': Passthrough + promptGuard: + request: + - response: + message: "Rejected due to inappropriate content" + regex: + action: Reject + matches: + - "credit card" + auth: + secretRef: + name: anthropic-secret +EOF +``` +{{% /tab %}} + +{{< /tabs >}} + +This configuration: +- **Routes** `/v1/messages` requests through Anthropic's native message processing +- **Passes through** all other endpoints without LLM processing +- **Rejects** any prompt containing "credit card" with a custom message + +--- + +## Step 9: Test the prompt guard + +Send the same prompt that was working before, but now with blocked content: + +```bash +ANTHROPIC_BASE_URL="http://localhost:8080" claude -p "What is a credit card" +``` + +The request is **rejected before reaching Anthropic**. You'll see the custom rejection message instead of a response from Claude. + +Now send a request without the blocked phrase: + +```bash +ANTHROPIC_BASE_URL="http://localhost:8080" claude -p "What is Kubernetes?" +``` + +This request goes through normally because it doesn't match any prompt guard patterns. + +--- + +## Extending prompt guards + +You can use built-in patterns and custom regex to enforce broader security policies: + +```yaml +promptGuard: + request: + - response: + message: "Request rejected: Contains sensitive information" + regex: + action: Reject + matches: + - "SSN" + - "Social Security" + - "delete all" + - "drop database" + - response: + message: "Request rejected: Contains email address" + regex: + action: Reject + builtins: + - Email + - CreditCard +``` + +| Pattern | What it blocks | +|---------|---------------| +| Custom regex (`matches`) | Any phrase you define β€” dangerous commands, sensitive terms | +| `Email` (builtin) | Email addresses in prompts | +| `CreditCard` (builtin) | Credit card numbers | +| `SSN` (builtin) | Social Security Numbers | + +--- + +## Real-world use cases + +| Scenario | Prompt Guard | Effect | +|----------|-------------|--------| +| **Prevent data exfiltration** | Block SSN, credit card, email patterns | PII never leaves the network | +| **Restrict dangerous operations** | Block "delete", "drop", "destroy" patterns | Prevent destructive agent actions | +| **Compliance enforcement** | Block proprietary terms, internal project names | Keep confidential data out of LLM providers | +| **Cost control** | Block overly long prompts (custom regex on length) | Prevent excessive token usage | + +--- + +## Cleanup + +```bash +kill %1 2>/dev/null +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/integrations/llm-clients/claude-code" title="Claude Code integration" subtitle="Quick setup without prompt guards" >}} + {{< card path="/llm/providers/anthropic" title="Anthropic provider" subtitle="Complete Anthropic provider configuration" >}} + {{< card path="/tutorials/prompt-enrichment" title="Prompt enrichment" subtitle="Inject context at the gateway layer" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/jwt-authorization/_index.md b/content/docs/kubernetes/1.1.x/tutorials/jwt-authorization/_index.md new file mode 100644 index 000000000..1fd3f6e88 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/jwt-authorization/_index.md @@ -0,0 +1,272 @@ +--- +title: JWT authorization +weight: 5 +description: Secure agentgateway with JWT authentication and fine-grained access control on Kubernetes +--- + +Secure your agentgateway LLM endpoints with JWT authentication using TrafficPolicy on Kubernetes. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster with agentgateway and an LLM backend +2. Create a TrafficPolicy with JWT authentication +3. Test that unauthenticated requests are rejected +4. Test that authenticated requests with a valid JWT are allowed + +## Before you begin + +Make sure you have the following tools installed: +- [Docker](https://www.docker.com/products/docker-desktop/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kind](https://kind.sigs.k8s.io/) +- [Helm](https://helm.sh/docs/intro/install/) +- An OpenAI API key (get one at [platform.openai.com](https://platform.openai.com/api-keys)) + +For detailed installation instructions, see the [LLM Gateway tutorial](../llm-gateway/). + +--- + +## Step 1: Create a kind cluster + +```bash +kind create cluster --name agentgateway +``` + +--- + +## Step 2: Install agentgateway + +```bash +# Gateway API CRDs +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml + +# agentgateway CRDs +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} + +# Control plane +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +--- + +## Step 3: Create a Gateway + +```bash +kubectl apply -f- <}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the proxy: + +```bash +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 4: Set up an LLM backend + +Set your API key and create the secret and backend: + +```bash +export OPENAI_API_KEY= + +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $OPENAI_API_KEY +--- +apiVersion: agentgateway.dev/v1alpha1 +kind: {{< reuse "agw-docs/snippets/backend.md" >}} +metadata: + name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + ai: + provider: + openai: + model: gpt-4.1-nano + policies: + auth: + secretRef: + name: openai-secret +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +--- + +## Step 5: Download the test JWT keys + +For this tutorial, use pre-generated test keys from the agentgateway repository: + +```bash +# Download the JWKS public key +curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/main/manifests/jwt/pub-key -o pub-key + +# Download a pre-generated test JWT token +curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/main/manifests/jwt/example1.key -o test-token.jwt +``` + +{{< callout type="warning" >}} +These are **test keys only**. For production, generate your own keys using tools like [step-cli](https://github.com/smallstep/cli). +{{< /callout >}} + +The test token contains these claims: + +```json +{ + "iss": "solo.io", + "sub": "alice", + "exp": 1900650294 +} +``` + +--- + +## Step 6: Create a JWT TrafficPolicy + +Create a TrafficPolicy that enforces JWT authentication on the Gateway. Requests without a valid JWT will be rejected. + +```bash +# Read the JWKS into a variable +JWKS=$(cat pub-key) + +kubectl apply -f- <}} +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: agentgateway-proxy + traffic: + jwtAuthentication: + mode: Strict + providers: + - issuer: solo.io + jwks: + inline: '$JWKS' + authorization: + action: Allow + policy: + matchExpressions: + - 'jwt.sub == "alice"' +EOF +``` + +This policy: +- **Requires** a valid JWT on every request (`mode: Strict`) +- **Validates** the token against the provided JWKS public key +- **Authorizes** only tokens where `sub` is `alice` + +--- + +## Step 7: Test the JWT authentication + +Set up port-forwarding: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +### Test without a token (should fail) + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{"model": "gpt-4.1-nano", "messages": [{"role": "user", "content": "Hello"}]}' +``` + +You should receive a `401 Unauthorized` response. + +### Test with a valid token (should succeed) + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $(cat test-token.jwt)" \ + -d '{"model": "gpt-4.1-nano", "messages": [{"role": "user", "content": "Hello! What is JWT?"}]}' | jq +``` + +You should receive a successful response from OpenAI. + +--- + +## Authorization rules + +The `matchExpressions` field uses CEL (Common Expression Language) for fine-grained access control: + +```yaml +# Allow only a specific user +matchExpressions: + - 'jwt.sub == "alice"' + +# Allow by email domain +matchExpressions: + - 'jwt.email.endsWith("@company.com")' + +# Allow by role claim +matchExpressions: + - '"admin" in jwt.roles' +``` + +--- + +## Cleanup + +```bash +kill %1 2>/dev/null +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/security/jwt/" title="JWT Reference" subtitle="Complete JWT configuration options" >}} + {{< card path="/mcp/tool-access" title="Tool Access Control" subtitle="Control MCP tool access with JWT claims" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/llm-gateway/_index.md b/content/docs/kubernetes/1.1.x/tutorials/llm-gateway/_index.md new file mode 100644 index 000000000..903d33a06 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/llm-gateway/_index.md @@ -0,0 +1,548 @@ +--- +title: LLM gateway +weight: 1 +description: Route requests to LLM providers through agentgateway on Kubernetes +--- + +Route requests to OpenAI, Anthropic, Google Gemini, and other LLM providers through a unified OpenAI-compatible API running on Kubernetes. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster using kind +2. Install the {{< reuse "agw-docs/snippets/kgateway.md" >}} control plane with agentgateway +3. Create a Gateway and configure an LLM provider backend +4. Route requests to your LLM provider through the agentgateway proxy +5. Test the setup with curl + +## Before you begin + +Make sure you have the following tools installed on your machine. + +### Docker + +kind runs Kubernetes inside Docker containers. Install Docker Desktop or Docker Engine for your operating system. + +{{< tabs items="macOS,Linux" >}} + +{{< tab >}} +```bash +# Install Docker Desktop for macOS +# Download from https://www.docker.com/products/docker-desktop/ +# Or via Homebrew: +brew install --cask docker +``` + +Start Docker Desktop and verify it's running: + +```bash +docker version +``` +{{< /tab >}} + +{{< tab >}} +```bash +# Install Docker Engine +curl -fsSL https://get.docker.com | sh +sudo systemctl start docker +sudo systemctl enable docker +``` + +Verify Docker is running: + +```bash +docker version +``` +{{< /tab >}} + +{{< /tabs >}} + +### kubectl + +```bash +# macOS +brew install kubectl + +# Linux +curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +chmod +x kubectl && sudo mv kubectl /usr/local/bin/ +``` + +Verify kubectl is installed: + +```bash +kubectl version --client +``` + +### kind + +```bash +# macOS +brew install kind + +# Linux +curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 +chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind +``` + +Verify kind is installed: + +```bash +kind version +``` + +### Helm + +```bash +# macOS +brew install helm + +# Linux +curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +Verify Helm is installed: + +```bash +helm version +``` + +--- + +## Step 1: Create a kind cluster + +Create a local Kubernetes cluster with kind. This cluster is where you will install agentgateway. + +```bash +kind create cluster --name agentgateway +``` + +Example output: + +```console +Creating cluster "agentgateway" ... + βœ“ Ensuring node image (kindest/node:v1.32.0) πŸ–Ό + βœ“ Preparing nodes πŸ“¦ + βœ“ Writing configuration πŸ“œ + βœ“ Starting control-plane πŸ•ΉοΈ + βœ“ Installing CNI πŸ”Œ + βœ“ Installing StorageClass πŸ’Ύ +Set kubectl context to "kind-agentgateway" +``` + +Verify the cluster is running: + +```bash +kubectl cluster-info --context kind-agentgateway +kubectl get nodes +``` + +--- + +## Step 2: Install the Kubernetes Gateway API CRDs + +Install the custom resources for the Kubernetes Gateway API. + +```bash +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml +``` + +Example output: + +```console +customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created +customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created +customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created +customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created +customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created +``` + +--- + +## Step 3: Install agentgateway CRDs + +Deploy the {{< reuse "agw-docs/snippets/kgateway.md" >}} CRDs using Helm. This creates the `{{< reuse "agw-docs/snippets/namespace.md" >}}` namespace and installs the custom resource definitions. + +```bash +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} +``` + +--- + +## Step 4: Install the agentgateway control plane + +Install the {{< reuse "agw-docs/snippets/kgateway.md" >}} control plane with Helm. + +```bash +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}}/ \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +Verify that the control plane is running: + +```bash +kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +Example output: + +```console +NAME READY STATUS RESTARTS AGE +agentgateway-78658959cd-cz6jt 1/1 Running 0 12s +``` + +Verify that the GatewayClass was created: + +```bash +kubectl get gatewayclass {{< reuse "agw-docs/snippets/gatewayclass.md" >}} +``` + +--- + +## Step 5: Create a Gateway + +Create a Gateway resource that sets up the agentgateway proxy with an HTTP listener. + +```bash +kubectl apply -f- <}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the Gateway and its proxy deployment to become ready: + +```bash +kubectl get gateway agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +Example output: + +```console +NAME CLASS ADDRESS PROGRAMMED AGE +agentgateway-proxy agentgateway True 30s + +NAME READY UP-TO-DATE AVAILABLE AGE +agentgateway-proxy 1/1 1 1 32s +``` + +--- + +## Step 6: Choose your LLM provider + +Set your API key, create a Kubernetes secret, and configure the LLM backend. + +{{< tabs items="OpenAI,Anthropic,Google Gemini" >}} + +{{< tab >}} + +### Set your API key + +```bash +export OPENAI_API_KEY= +``` + +### Create the Kubernetes secret + +```bash +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $OPENAI_API_KEY +EOF +``` + +### Create the LLM backend + +```bash +kubectl apply -f- <}} +metadata: + name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + ai: + provider: + openai: + model: gpt-4.1-nano + policies: + auth: + secretRef: + name: openai-secret +EOF +``` + +### Create the HTTPRoute + +```bash +kubectl apply -f- <}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +{{< /tab >}} + +{{< tab >}} + +### Set your API key + +```bash +export ANTHROPIC_API_KEY= +``` + +### Create the Kubernetes secret + +```bash +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $ANTHROPIC_API_KEY +EOF +``` + +### Create the LLM backend + +```bash +kubectl apply -f- <}} +metadata: + name: anthropic + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + ai: + provider: + anthropic: + model: claude-sonnet-4-20250514 + policies: + auth: + secretRef: + name: anthropic-secret +EOF +``` + +### Create the HTTPRoute + +```bash +kubectl apply -f- <}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: anthropic + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +{{< /tab >}} + +{{< tab >}} + +### Set your API key + +```bash +export GEMINI_API_KEY= +``` + +### Create the Kubernetes secret + +```bash +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $GEMINI_API_KEY +EOF +``` + +### Create the LLM backend + +```bash +kubectl apply -f- <}} +metadata: + name: gemini + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + ai: + provider: + gemini: + model: gemini-2.0-flash + policies: + auth: + secretRef: + name: gemini-secret +EOF +``` + +### Create the HTTPRoute + +```bash +kubectl apply -f- <}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: gemini + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +{{< /tab >}} + +{{< /tabs >}} + +--- + +## Step 7: Test the API + +Set up port-forwarding to access the agentgateway proxy from your local machine: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +Send a request to the LLM provider through agentgateway: + +{{< tabs items="OpenAI,Anthropic,Google Gemini" >}} + +{{< tab >}} +```bash +curl "localhost:8080/v1/chat/completions" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4.1-nano", + "messages": [{"role": "user", "content": "Hello! What is Kubernetes in one sentence?"}] + }' | jq +``` +{{< /tab >}} + +{{< tab >}} +```bash +curl "localhost:8080/v1/messages" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "Hello! What is Kubernetes in one sentence?"}] + }' | jq +``` +{{< /tab >}} + +{{< tab >}} +```bash +curl "localhost:8080/v1/chat/completions" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gemini-2.0-flash", + "messages": [{"role": "user", "content": "Hello! What is Kubernetes in one sentence?"}] + }' | jq +``` +{{< /tab >}} + +{{< /tabs >}} + +Example output: + +```json +{ + "id": "chatcmpl-...", + "object": "chat.completion", + "choices": [{ + "message": { + "role": "assistant", + "content": "Kubernetes is an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications." + }, + "index": 0, + "finish_reason": "stop" + }] +} +``` + +--- + +## Cleanup + +When you're done, stop port-forwarding and delete the kind cluster: + +```bash +# Stop port-forward (if running in background) +kill %1 2>/dev/null + +# Delete the kind cluster +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/llm/" title="LLM Overview" subtitle="Learn more about LLM gateway features on Kubernetes" >}} + {{< card path="/llm/providers/" title="More Providers" subtitle="Configure additional LLM providers" >}} + {{< card path="/security/" title="Security" subtitle="Secure your agentgateway deployment" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/mcp-federation/_index.md b/content/docs/kubernetes/1.1.x/tutorials/mcp-federation/_index.md new file mode 100644 index 000000000..0f89ad56d --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/mcp-federation/_index.md @@ -0,0 +1,311 @@ +--- +title: MCP federation +weight: 3 +description: Federate tools from multiple MCP servers through a single endpoint on Kubernetes +--- + +Expose a single MCP endpoint that aggregates tools from multiple backend MCP servers with unified routing on Kubernetes. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster with agentgateway +2. Deploy two separate MCP servers (echo and filesystem) +3. Federate both servers into a single {{< reuse "agw-docs/snippets/backend.md" >}} +4. Access tools from both servers through one unified endpoint + +## Before you begin + +Make sure you have the following tools installed: +- [Docker](https://www.docker.com/products/docker-desktop/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kind](https://kind.sigs.k8s.io/) +- [Helm](https://helm.sh/docs/intro/install/) + +For detailed installation instructions, see the [LLM Gateway tutorial](../llm-gateway/). + +--- + +## Step 1: Create a kind cluster + +```bash +kind create cluster --name agentgateway +``` + +--- + +## Step 2: Install agentgateway + +```bash +# Gateway API CRDs +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml + +# agentgateway CRDs +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} + +# Control plane +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +Verify the control plane is running: + +```bash +kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 3: Create a Gateway + +```bash +kubectl apply -f- <}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the proxy to be ready: + +```bash +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 4: Deploy the MCP servers + +Deploy two separate MCP servers that provide different sets of tools. + +### MCP "Everything" server + +This server provides general-purpose tools like `echo`, `add`, and `longRunningOperation`. + +```bash +kubectl apply -f- <}} +spec: + replicas: 1 + selector: + matchLabels: + app: mcp-everything + template: + metadata: + labels: + app: mcp-everything + spec: + containers: + - name: mcp-server + image: node:22-alpine + command: ["npx", "-y", "mcp-proxy", "--port", "8080", "--", "npx", "-y", "@modelcontextprotocol/server-everything"] + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: mcp-everything + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + selector: + app: mcp-everything + ports: + - port: 80 + targetPort: 8080 + appProtocol: agentgateway.dev/mcp +EOF +``` + +### MCP "Filesystem" server + +This server provides file operation tools like `read_file`, `write_file`, and `list_directory`. + +```bash +kubectl apply -f- <}} +spec: + replicas: 1 + selector: + matchLabels: + app: mcp-filesystem + template: + metadata: + labels: + app: mcp-filesystem + spec: + containers: + - name: mcp-server + image: node:22-alpine + command: ["npx", "-y", "mcp-proxy", "--port", "8080", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"] + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: mcp-filesystem + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + selector: + app: mcp-filesystem + ports: + - port: 80 + targetPort: 8080 + appProtocol: agentgateway.dev/mcp +EOF +``` + +Wait for both pods to be ready: + +```bash +kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} -l 'app in (mcp-everything, mcp-filesystem)' -w +``` + +--- + +## Step 5: Create the federated backend + +Create a single {{< reuse "agw-docs/snippets/backend.md" >}} that targets both MCP servers. Tools from each server are automatically prefixed with their target name. + +```bash +kubectl apply -f- <}} +metadata: + name: mcp-federated + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + mcp: + targets: + - name: everything + static: + host: mcp-everything.{{< reuse "agw-docs/snippets/namespace.md" >}}.svc.cluster.local + port: 80 + protocol: SSE + - name: filesystem + static: + host: mcp-filesystem.{{< reuse "agw-docs/snippets/namespace.md" >}}.svc.cluster.local + port: 80 + protocol: SSE +EOF +``` + +--- + +## Step 6: Create the HTTPRoute + +```bash +kubectl apply -f- <}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: mcp-federated + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +--- + +## Step 7: Test the federated endpoint + +Set up port-forwarding: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +Initialize an MCP session: + +```bash +curl -s -i http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' +``` + +Copy the `mcp-session-id` from the response headers, then list all available tools: + +```bash +curl -s http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -H "Mcp-Session-Id: YOUR_SESSION_ID" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":2}' +``` + +You should see tools from **both** servers, prefixed with their target name: +- `everything_echo`, `everything_add`, `everything_longRunningOperation`, ... +- `filesystem_read_file`, `filesystem_write_file`, `filesystem_list_directory`, ... + +### Call a tool from each server + +Call the echo tool from the "everything" server: + +```bash +curl -s http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -H "Mcp-Session-Id: YOUR_SESSION_ID" \ + -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"everything_echo","arguments":{"message":"Hello from federation!"}},"id":3}' +``` + +Call the list_directory tool from the "filesystem" server: + +```bash +curl -s http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -H "Mcp-Session-Id: YOUR_SESSION_ID" \ + -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"filesystem_list_directory","arguments":{"path":"/tmp"}},"id":4}' +``` + +--- + +## Cleanup + +```bash +kill %1 2>/dev/null +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/tutorials/jwt-authorization" title="JWT Authorization" subtitle="Secure with JWT authentication" >}} + {{< card path="/mcp/" title="MCP Documentation" subtitle="Complete MCP configuration reference" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/prompt-enrichment/_index.md b/content/docs/kubernetes/1.1.x/tutorials/prompt-enrichment/_index.md new file mode 100644 index 000000000..7c80e408b --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/prompt-enrichment/_index.md @@ -0,0 +1,454 @@ +--- +title: Prompt enrichment +weight: 9 +description: Inject context at the gateway layer to improve LLM output accuracy on Kubernetes +--- + +Improve LLM output accuracy by injecting system and user prompts at the gateway layer using {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resources on Kubernetes. This centralizes prompt management so every request gets the right context without changing application code. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster with agentgateway and an LLM backend +2. Send a request without prompt enrichment and observe the raw response +3. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} to automatically prepend system prompts to every request +4. See how gateway-injected prompts transform unstructured text into structured CSV format +5. Override the gateway prompt on a per-request basis + +## Before you begin + +Make sure you have the following tools installed: +- [Docker](https://www.docker.com/products/docker-desktop/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kind](https://kind.sigs.k8s.io/) +- [Helm](https://helm.sh/docs/intro/install/) +- An API key for one of the supported LLM providers (OpenAI or Anthropic) + +For detailed installation instructions, see the [LLM Gateway tutorial](../llm-gateway/). + +--- + +## Why prompt enrichment? + +LLMs produce better, more consistent results when given clear context. Instead of relying on every client to include the right system prompt, you can inject prompts at the gateway layer: + +- **Consistency** β€” Every request gets the same baseline instructions, regardless of client +- **Centralized management** β€” Update prompts in one place instead of in every app +- **Separation of concerns** β€” App developers focus on user content; platform teams manage prompt policies +- **Per-route customization** β€” Different routes can have different prompt policies + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Client β”‚ β”‚ agentgateway β”‚ β”‚ LLM Provider β”‚ +β”‚ (no system β”‚ ──── β”‚ + prepends system prompt β”‚ ──── β”‚ (receives full β”‚ +β”‚ prompt) β”‚ β”‚ from AgentgatewayPolicy β”‚ β”‚ context) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## Step 1: Create a kind cluster + +```bash +kind create cluster --name agentgateway +``` + +--- + +## Step 2: Install agentgateway + +```bash +# Gateway API CRDs +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml + +# agentgateway CRDs +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} + +# Control plane +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +Verify the control plane is running: + +```bash +kubectl get pods -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 3: Create a Gateway + +```bash +kubectl apply -f- <}} + labels: + app: agentgateway +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the proxy: + +```bash +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +--- + +## Step 4: Set up the LLM backend + +Choose your LLM provider and set your API key: + +{{< tabs items="OpenAI,Anthropic" >}} + +{{% tab tabName="OpenAI" %}} +```bash +export OPENAI_API_KEY= + +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $OPENAI_API_KEY +--- +apiVersion: agentgateway.dev/v1alpha1 +kind: {{< reuse "agw-docs/snippets/backend.md" >}} +metadata: + name: llm + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + ai: + provider: + openai: + model: gpt-4.1-nano + policies: + auth: + secretRef: + name: llm-secret +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: llm + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: llm + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` +{{% /tab %}} + +{{% tab tabName="Anthropic" %}} +```bash +export ANTHROPIC_API_KEY= + +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $ANTHROPIC_API_KEY +--- +apiVersion: agentgateway.dev/v1alpha1 +kind: {{< reuse "agw-docs/snippets/backend.md" >}} +metadata: + name: llm + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + ai: + provider: + anthropic: + model: claude-sonnet-4-5-20250929 + policies: + auth: + secretRef: + name: llm-secret +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: llm + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: llm + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` +{{% /tab %}} + +{{< /tabs >}} + +--- + +## Step 5: Test without prompt enrichment + +Set up port-forwarding: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +Send a request with unstructured text and no system prompt: + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "user", + "content": "Seattle, Los Angeles, and Chicago are cities in North America. London, Paris, and Berlin are cities in Europe." + } + ] + }' | jq -r '.choices[].message.content' +``` + +Without a system prompt, the LLM treats this as a conversational input. You'll get a freeform response β€” maybe a summary, maybe a question, maybe a list. The output is **unpredictable** because the model has no instructions on what format to use. + +--- + +## Step 6: Add prompt enrichment with an AgentgatewayPolicy + +Now create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} that automatically prepends a system prompt to every request on the `llm` HTTPRoute. This tells the LLM to parse unstructured text into CSV format. + +```bash +kubectl apply -f- <}} +kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} +metadata: + name: prompt-enrichment + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: llm + backend: + ai: + prompt: + prepend: + - role: system + content: "Parse the unstructured text into CSV format." +EOF +``` + +This policy: +- **Targets** the `llm` HTTPRoute (not the Gateway, enabling per-route customization) +- **Prepends** a system prompt before the user's message +- **Applies automatically** to every request on that route + +--- + +## Step 7: Test with prompt enrichment + +Send the exact same request as before β€” no system prompt in the request itself: + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "user", + "content": "Seattle, Los Angeles, and Chicago are cities in North America. London, Paris, and Berlin are cities in Europe." + } + ] + }' | jq -r '.choices[].message.content' +``` + +This time, the response is structured CSV: + +``` +City,Continent +Seattle,North America +Los Angeles,North America +Chicago,North America +London,Europe +Paris,Europe +Berlin,Europe +``` + +The system prompt was automatically injected by the gateway before the request reached the LLM. + +### Try another request + +Send different unstructured text. The gateway-injected CSV instruction applies to every request: + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "user", + "content": "The recipe called for eggs, flour and sugar. The price was $5, $3, and $2." + } + ] + }' | jq -r '.choices[].message.content' +``` + +Expected output: + +``` +Item,Price +Eggs,$5 +Flour,$3 +Sugar,$2 +``` + +--- + +## Step 8: Override the prompt per-request + +What if a client needs a different output format? Including a system prompt in the request overrides the gateway-injected prompt: + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "system", + "content": "Parse the unstructured content and return a JSON array of objects." + }, + { + "role": "user", + "content": "The recipe called for eggs, flour and sugar. The price was $5, $3, and $2." + } + ] + }' | jq -r '.choices[].message.content' +``` + +Now the response comes back as JSON instead of CSV: + +```json +[ + {"ingredient": "eggs", "price": "$5"}, + {"ingredient": "flour", "price": "$3"}, + {"ingredient": "sugar", "price": "$2"} +] +``` + +Send the same request without the system prompt, and it falls back to the gateway's CSV policy: + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "user", + "content": "The recipe called for eggs, flour and sugar. The price was $5, $3, and $2." + } + ] + }' | jq -r '.choices[].message.content' +``` + +--- + +## Advanced: User-level prompt enrichment + +You can also prepend user-level prompts. This is useful for adding behavioral constraints like "be concise" or "always ask for confirmation": + +```yaml +kubectl apply -f- <}} +kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} +metadata: + name: prompt-enrichment + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + labels: + app: agentgateway +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: llm + backend: + ai: + prompt: + prepend: + - role: system + content: "You are a senior software engineering assistant with deep expertise in Kubernetes and cloud-native systems." + - role: user + content: "Always explain things concisely and include practical examples." +EOF +``` + +Now every request gets both a system persona and a behavioral instruction before the user's actual message. + +--- + +## Real-world use cases + +| Use Case | System Prompt | Effect | +|----------|--------------|--------| +| **Customer support** | "You are a helpful support agent. Always be polite and end by asking for a rating." | Consistent tone across all agents | +| **Code review** | "You are a code reviewer. Focus on security, performance, and Go best practices." | Specialized expertise per route | +| **Data parsing** | "Parse unstructured text into the requested format (CSV, JSON, or XML)." | Structured output by default | +| **Compliance** | "Never reveal internal system details. Redact any PII in responses." | Enforced guardrails | + +--- + +## Cleanup + +```bash +kill %1 2>/dev/null +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/llm/prompt-enrichment" title="Prompt Enrichment Reference" subtitle="Complete configuration options" >}} + {{< card path="/tutorials/llm-gateway" title="LLM Gateway" subtitle="Route to multiple providers" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/1.1.x/tutorials/telemetry/_index.md b/content/docs/kubernetes/1.1.x/tutorials/telemetry/_index.md new file mode 100644 index 000000000..31a22ae00 --- /dev/null +++ b/content/docs/kubernetes/1.1.x/tutorials/telemetry/_index.md @@ -0,0 +1,462 @@ +--- +title: Telemetry & observability +weight: 6 +description: Enable tracing, prompt logging, cost tracking, and metrics for agentgateway on Kubernetes +test: + telemetry: + - file: content/docs/kubernetes/latest/tutorials/telemetry/_index.md + path: telemetry +--- + +Enable distributed tracing and metrics collection for agentgateway on Kubernetes using OpenTelemetry and Jaeger. Get prompt logging, cost tracking, and an audit trail: traces include LLM request/response data and token usage for each request. + +## What you'll build + +In this tutorial, you will: + +1. Set up a local Kubernetes cluster with agentgateway and an LLM backend +2. Deploy Jaeger for trace collection and visualization +3. Configure an AgentgatewayPolicy to enable distributed tracing +4. Send requests and view traces in the Jaeger UI + +## Before you begin + +Make sure you have the following tools installed: +- [Docker](https://www.docker.com/products/docker-desktop/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kind](https://kind.sigs.k8s.io/) +- [Helm](https://helm.sh/docs/intro/install/) +- An OpenAI API key (get one at [platform.openai.com](https://platform.openai.com/api-keys)) + +For detailed installation instructions, see the [LLM Gateway tutorial](../llm-gateway/). + +--- + +## Step 1: Create a kind cluster + +```bash +kind create cluster --name agentgateway +``` + +--- + +## Step 2: Install agentgateway + +```bash {paths="telemetry"} +# Gateway API CRDs +kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version.md" >}}/standard-install.yaml + +# agentgateway CRDs +helm upgrade -i --create-namespace \ + --namespace {{< reuse "agw-docs/snippets/namespace.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway-crds.md" >}} {{< reuse "agw-docs/snippets/helm-path-crds.md" >}} + +# Control plane +helm upgrade -i -n {{< reuse "agw-docs/snippets/namespace.md" >}} {{< reuse "agw-docs/snippets/helm-kgateway.md" >}} {{< reuse "agw-docs/snippets/helm-path.md" >}} \ + --version {{< reuse "agw-docs/versions/helm-version-flag.md" >}} +``` + +--- + +## Step 3: Create a Gateway + +```bash {paths="telemetry"} +kubectl apply -f- <}} +spec: + gatewayClassName: {{< reuse "agw-docs/snippets/agw-gatewayclass.md" >}} + listeners: + - protocol: HTTP + port: 80 + name: http + allowedRoutes: + namespaces: + from: All +EOF +``` + +Wait for the proxy: + +```bash +kubectl get deployment agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} +``` + +{{< doc-test paths="telemetry" >}} +YAMLTest -f - <<'EOF' +- name: wait for agentgateway-proxy deployment to be ready + wait: + target: + kind: Deployment + metadata: + namespace: agentgateway-system + name: agentgateway-proxy + jsonPath: "$.status.availableReplicas" + jsonPathExpectation: + comparator: greaterThan + value: 0 + polling: + timeoutSeconds: 300 + intervalSeconds: 5 + +- name: wait for agentgateway-proxy service LB address + wait: + target: + kind: Service + metadata: + namespace: agentgateway-system + name: agentgateway-proxy + jsonPath: "$.status.loadBalancer.ingress[0].ip" + jsonPathExpectation: + comparator: exists + polling: + timeoutSeconds: 300 + intervalSeconds: 5 + setVars: + INGRESS_GW_ADDRESS: + value: true +EOF +{{< /doc-test >}} + +{{< doc-test paths="telemetry" >}} +export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-proxy -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") +{{< /doc-test >}} + +--- + +## Step 4: Deploy Jaeger + +Deploy Jaeger as a trace collector and visualization tool in its own namespace. + +```bash {paths="telemetry"} +kubectl create namespace telemetry + +kubectl apply -n telemetry -f- <}} +YAMLTest -f - <<'EOF' +- name: wait for jaeger deployment to be ready + wait: + target: + kind: Deployment + metadata: + namespace: telemetry + name: jaeger + jsonPath: "$.status.availableReplicas" + jsonPathExpectation: + comparator: greaterThan + value: 0 + polling: + timeoutSeconds: 300 + intervalSeconds: 5 +EOF +{{< /doc-test >}} + +--- + +## Step 5: Configure tracing with an AgentgatewayPolicy + +Create an AgentgatewayPolicy that sends traces to the Jaeger collector. + +```bash {paths="telemetry"} +kubectl apply -f- <}} +spec: + targetRefs: + - kind: Gateway + name: agentgateway-proxy + group: gateway.networking.k8s.io + frontend: + tracing: + backendRef: + name: jaeger + namespace: telemetry + port: 4317 + protocol: GRPC + randomSampling: "true" +EOF +``` + +This policy: +- **Targets** the Gateway to apply tracing to all routes +- **Sends traces** to Jaeger via OTLP gRPC on port 4317 +- **Samples all requests** (`randomSampling: "true"`) for development + +--- + +## Step 6: Set up an LLM backend + +{{< doc-test paths="telemetry" >}} +export OPENAI_API_KEY=${OPENAI_API_KEY:-placeholder} + +kubectl apply -f- <}} + +{{< doc-test paths="telemetry" >}} +YAMLTest -f - <<'EOF' +- name: wait for openai backend to be accepted + wait: + target: + kind: AgentgatewayBackend + metadata: + namespace: agentgateway-system + name: openai + jsonPath: "$.status.conditions[?(@.type=='Accepted')].status" + jsonPathExpectation: + comparator: equals + value: "True" + polling: + timeoutSeconds: 60 + intervalSeconds: 2 +EOF +{{< /doc-test >}} + +{{< doc-test paths="telemetry" >}} +YAMLTest -f - <<'EOF' +- name: send request to OpenAI through agentgateway with tracing + retries: 1 + http: + url: "http://${INGRESS_GW_ADDRESS}:80/v1/chat/completions" + method: POST + headers: + content-type: application/json + body: | + { + "model": "gpt-4.1-nano", + "messages": [{"role": "user", "content": "Say hello in one word"}] + } + source: + type: local + expect: + statusCode: 200 +EOF +{{< /doc-test >}} + +```bash +export OPENAI_API_KEY= + +kubectl apply -f- <}} +type: Opaque +stringData: + Authorization: $OPENAI_API_KEY +--- +apiVersion: agentgateway.dev/v1alpha1 +kind: {{< reuse "agw-docs/snippets/backend.md" >}} +metadata: + name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + ai: + provider: + openai: + model: gpt-4.1-nano + policies: + auth: + secretRef: + name: openai-secret +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} +spec: + parentRefs: + - name: agentgateway-proxy + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + rules: + - backendRefs: + - name: openai + namespace: {{< reuse "agw-docs/snippets/namespace.md" >}} + group: agentgateway.dev + kind: {{< reuse "agw-docs/snippets/backend.md" >}} +EOF +``` + +--- + +## Step 7: Generate traces + +Set up port-forwarding for the agentgateway proxy: + +```bash +kubectl port-forward deployment/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 8080:80 & +``` + +Send a few requests to generate traces: + +```bash +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4.1-nano", + "messages": [{"role": "user", "content": "What is OpenTelemetry?"}] + }' | jq + +curl -s http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4.1-nano", + "messages": [{"role": "user", "content": "What is distributed tracing?"}] + }' | jq +``` + +--- + +## Step 8: View traces in Jaeger + +Set up port-forwarding for the Jaeger UI: + +```bash +kubectl port-forward -n telemetry svc/jaeger 16686:16686 & +``` + +Open the Jaeger UI at [http://localhost:16686](http://localhost:16686). + +1. Select **agentgateway** from the **Service** dropdown +2. Click **Find Traces** +3. Click on a trace to see the full request flow + +You'll see spans for: +- The incoming HTTP request +- LLM provider routing +- Backend request to OpenAI +- Response processing + +Each span includes details like: +- Request and response token counts +- Model information +- Latency breakdown + +--- + +## Production sampling + +For production, use ratio-based sampling to reduce overhead: + +```yaml +frontend: + tracing: + backendRef: + name: otel-collector + namespace: telemetry + port: 4317 + protocol: GRPC + randomSampling: "0.1" # Sample 10% of traces +``` + +--- + +## Cleanup + +```bash +kill %1 %2 2>/dev/null +kind delete cluster --name agentgateway +``` + +--- + +## Next steps + +{{< cards >}} + {{< card path="/observability/" title="Observability Reference" subtitle="Complete observability configuration" >}} + {{< card path="/observability/otel-stack" title="OTel Stack" subtitle="Full OpenTelemetry stack setup" >}} + {{< card path="/tutorials/jwt-authorization" title="JWT Authorization" subtitle="Add security to your deployment" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/latest/_index.md b/content/docs/kubernetes/latest/_index.md index b7fea4acd..b8fd26d8f 100644 --- a/content/docs/kubernetes/latest/_index.md +++ b/content/docs/kubernetes/latest/_index.md @@ -1,5 +1,5 @@ --- -title: Latest +title: Version 1.2.x weight: 460 description: Use agentgateway in a Kubernetes environment. test: skip diff --git a/content/docs/kubernetes/latest/about/_index.md b/content/docs/kubernetes/latest/about/_index.md index 30c2b161a..d89289e81 100644 --- a/content/docs/kubernetes/latest/about/_index.md +++ b/content/docs/kubernetes/latest/about/_index.md @@ -5,5 +5,7 @@ icon: info test: skip --- -{{< reuse "agw-docs/snippets/agentgateway/about.md" >}} To learn more about {{< reuse "agw-docs/snippets/agentgateway.md" >}}, review the following topics. +{{< reuse "agw-docs/snippets/agentgateway/about.md" >}} + +To learn more about {{< reuse "agw-docs/snippets/agentgateway.md" >}}, review the following topics. diff --git a/content/docs/kubernetes/latest/about/policies/_index.md b/content/docs/kubernetes/latest/about/policies/_index.md new file mode 100644 index 000000000..586d0a0c2 --- /dev/null +++ b/content/docs/kubernetes/latest/about/policies/_index.md @@ -0,0 +1,7 @@ +--- +title: Policies +weight: 25 +description: Learn about how AgentgatewayPolicy sections target different Kubernetes resource types, and how policies are merged when multiple policies apply to the same resource. +test: skip +--- + diff --git a/content/docs/kubernetes/latest/about/policies/conditional-policies.md b/content/docs/kubernetes/latest/about/policies/conditional-policies.md new file mode 100644 index 000000000..50fdf563e --- /dev/null +++ b/content/docs/kubernetes/latest/about/policies/conditional-policies.md @@ -0,0 +1,8 @@ +--- +title: Conditional policies +weight: 15 +description: Run different variants of a policy on the same route based on a CEL expression evaluated against the request. +test: skip +--- + +{{< reuse "agw-docs/pages/conditional-policies.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/latest/about/policies/filter-order.md b/content/docs/kubernetes/latest/about/policies/filter-order.md new file mode 100644 index 000000000..930dcb502 --- /dev/null +++ b/content/docs/kubernetes/latest/about/policies/filter-order.md @@ -0,0 +1,8 @@ +--- +title: Policy processing order +weight: 10 +description: Review the order in which policies and traffic filters are processed. +test: skip +--- + +{{< reuse "agw-docs/pages/agentgateway/about/processing-order.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/latest/about/policies/overview.md b/content/docs/kubernetes/latest/about/policies/overview.md new file mode 100644 index 000000000..8d0fdfc80 --- /dev/null +++ b/content/docs/kubernetes/latest/about/policies/overview.md @@ -0,0 +1,28 @@ +--- +title: Overview +weight: 1 +description: Learn about the different policy sections. +test: skip +--- + +The {{< reuse "agw-docs/snippets/agentgateway/agentgatewaypolicy.md" >}} custom resource lets you apply traffic management, security, observability, and backend connection policies to your agentgateway resources. + +## Policy sections + +Each {{< reuse "agw-docs/snippets/agentgateway/agentgatewaypolicy.md" >}} has three top-level sections in the `spec` field that control different stages of request processing. You can include one or more of these sections in a single policy. + +| Section | Description | Available fields | +| -- | -- | -- | +| `frontend` | Controls how the gateway accepts incoming connections. Applies at the gateway level before routing decisions. | `tcp`, `tls`, `http`, `networkAuthorization`, `accessLog`, `tracing` | +| `traffic` | Controls how agentgateway processes traffic. Applies at the listener, route, or route rule level. Fields are listed in execution order. | `cors`, `jwtAuthentication`, `basicAuthentication`, `apiKeyAuthentication`, `extAuth`, `authorization`, `rateLimit`, `extProc`, `transformation`, `csrf`, `headerModifiers`, `hostRewrite`, `directResponse`, `timeouts`, `retry` | +| `backend` | Controls how agentgateway connects to destination backends. Applies at the backend, service, route, or gateway level. | `tcp`, `tls`, `http`, `tunnel`, `transformation`, `auth`, `extAuth`, `health`, `ai`, `mcp` | + +## Example guides + +Check out the following sections for policy examples. + +{{< cards >}} + {{< card path="/security" title="Security" >}} + {{< card path="/traffic-management" title="Traffic management" >}} + {{< card path="/resiliency" title="Resiliency" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/latest/about/policies/target-merge.md b/content/docs/kubernetes/latest/about/policies/target-merge.md new file mode 100644 index 000000000..66a868fbc --- /dev/null +++ b/content/docs/kubernetes/latest/about/policies/target-merge.md @@ -0,0 +1,55 @@ +--- +title: Targeting and merging +weight: 10 +description: Learn how to target and merge policies when multiple policies apply to the same resource. +test: skip +--- + +## Policy targeting {#targeting} + +Each policy section can only target specific Kubernetes resource types. If you set a `targetRef` or `targetSelector` to an invalid resource type for the policy section, the Kubernetes API server rejects the request with a validation error. Invalid targeting is **not** silently ignored. + +{{< callout type="info">}} +A single {{< reuse "agw-docs/snippets/agentgateway/agentgatewaypolicy.md" >}} can only target one kind of resource. For example, you cannot target both a Gateway and an HTTPRoute in the same policy. All entries in `targetRefs` or `targetSelectors` must reference the same `kind`. +{{< /callout >}} + +### Targeting rules + +The following table shows which resource types each policy section can target. + +| Policy section | Valid targets | sectionName | Notes | +| -- | -- | -- | -- | +| `frontend` | Gateway | Not allowed | Applies to all listeners on the targeted Gateway. | +| `traffic` | Gateway, HTTPRoute, GRPCRoute, ListenerSet | Optional | When targeting a Gateway, the `sectionName` selects a specific listener. When targeting an HTTPRoute or GRPCRoute, the `sectionName` selects a specific route rule. | +| `backend` | Gateway, HTTPRoute, GRPCRoute, ListenerSet, Service, {{< reuse "agw-docs/snippets/agentgateway/agentgatewaybackend.md" >}} | Optional | When targeting a Service, the `sectionName` selects a specific port. When targeting an {{< reuse "agw-docs/snippets/agentgateway/agentgatewaybackend.md" >}}, the `sectionName` selects a specific sub-backend. | + +### Backend section restrictions + +Some `backend` sub-fields have additional targeting restrictions. + +| Field | Restriction | +| -- | -- | +| `backend.ai` | Cannot target a Service. Use an {{< reuse "agw-docs/snippets/agentgateway/agentgatewaybackend.md" >}} instead. | +| `backend.mcp` | Cannot target a Service. Use an {{< reuse "agw-docs/snippets/agentgateway/agentgatewaybackend.md" >}} instead. | + +### Traffic phase restrictions + +The `traffic` section supports an optional `phase` field that controls when the policy runs. When you set the phase to `PreRouting`, the policy runs before route selection. Because of this timing, `PreRouting` policies can only target a Gateway or ListenerSet. + +For more information, see [Policy processing order](#processing-order) and [PreRouting filters](#prerouting). + +## Policy merging {#merging} + +When multiple policies target the same resource, agentgateway merges the policy sections on a **field level** (shallow merge). If two policies set the same field, the more specific policy takes precedence. + +### Merge precedence {#merging-precedence} + +Each policy section follows a different precedence order based on the specificity of the target. The more specific the target, the higher the precedence. + +| Section | Precedence order (lowest to highest) | +| -- | -- | +| `frontend` | Field-level merge across policies that target the same Gateway. | +| `traffic` | Gateway < Listener < Route < Route rule | +| `backend` | Gateway < Listener < Route (targetRef) < Route rule (targetRef) < Backend (targetRef) < Backend (inline on the backend object) < Route backend ref (inline on the route) | + +For example, if a Gateway-level policy sets `backend.tcp` and `backend.tls`, and a Backend-level policy sets `backend.tls`, the effective policy uses `tcp` from the Gateway policy and `tls` from the Backend policy. \ No newline at end of file diff --git a/content/docs/kubernetes/latest/install/advanced.md b/content/docs/kubernetes/latest/install/advanced.md index cf11521a7..e7d7db7ab 100644 --- a/content/docs/kubernetes/latest/install/advanced.md +++ b/content/docs/kubernetes/latest/install/advanced.md @@ -111,6 +111,18 @@ commonLabels: agw-managed: "true" ``` +## PodDisruptionBudget + +Configure a Pod Disruption Budget to ensure that a minimum number of control plane instances are up and running at any given time during voluntary disruptions, such as upgrades. In this example, 50% of your control plane instances must be running. + +```yaml + +controller: + podDisruptionBudget: + minAvailable: 50% +``` + + diff --git a/content/docs/kubernetes/latest/integrations/llm-observability/_index.md b/content/docs/kubernetes/latest/integrations/llm-observability/_index.md index ed57028ac..8b63ec35a 100644 --- a/content/docs/kubernetes/latest/integrations/llm-observability/_index.md +++ b/content/docs/kubernetes/latest/integrations/llm-observability/_index.md @@ -7,7 +7,6 @@ test: skip Agentgateway can send LLM telemetry to specialized observability platforms for prompt logging (request/response logging), cost tracking, audit trail, and performance monitoring. - ## How it works Agentgateway exports LLM telemetry via OpenTelemetry, which can be forwarded to LLM-specific observability platforms. These platforms provide the following. diff --git a/content/docs/kubernetes/latest/llm/guardrails/_index.md b/content/docs/kubernetes/latest/llm/guardrails/_index.md index 98a23ac37..8ebf41a48 100644 --- a/content/docs/kubernetes/latest/llm/guardrails/_index.md +++ b/content/docs/kubernetes/latest/llm/guardrails/_index.md @@ -7,3 +7,8 @@ test: skip Guardrails are security policies that inspect LLM requests and responses to detect and block harmful, policy-violating, or inappropriate content before it reaches the model or the user. You can apply prompt guards to the request phase, the response phase, or both. +To learn more about guardrails, see the following topic. + +To set up guardrails, check out the following guides. + +To track guardrails and content safety, see the following guide. diff --git a/content/docs/kubernetes/latest/llm/providers/_index.md b/content/docs/kubernetes/latest/llm/providers/_index.md index 010541e41..85e8f0bcf 100644 --- a/content/docs/kubernetes/latest/llm/providers/_index.md +++ b/content/docs/kubernetes/latest/llm/providers/_index.md @@ -6,3 +6,7 @@ test: skip --- Learn how to configure agentgateway for a particular LLM. + +## OpenAI-compatible providers + +Popular OpenAI-compatible providers include Mistral, DeepSeek, Groq, Together AI, Perplexity, and Fireworks AI. diff --git a/content/docs/kubernetes/latest/observability/_index.md b/content/docs/kubernetes/latest/observability/_index.md index eb3f06efb..11d05f8fa 100644 --- a/content/docs/kubernetes/latest/observability/_index.md +++ b/content/docs/kubernetes/latest/observability/_index.md @@ -11,6 +11,7 @@ test: skip Gain insight into the health and performance of your gateway environment. Get prompt logging, cost tracking, and an audit trail for LLM traffic via OpenTelemetry. + ## External observability platforms For specialized LLM observability platforms with prompt logging, cost tracking, and evaluation capabilities, see the [LLM Observability integrations]({{< link-hextra path="/integrations/llm-observability/" >}}). diff --git a/content/docs/kubernetes/latest/operations/agctl.md b/content/docs/kubernetes/latest/operations/agctl.md new file mode 100644 index 000000000..7d23aa71a --- /dev/null +++ b/content/docs/kubernetes/latest/operations/agctl.md @@ -0,0 +1,12 @@ +--- +title: Install agctl +weight: 5 +description: Install the agctl command-line tool for inspecting and debugging agentgateway. +test: skip +--- + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} + +{{< reuse "agw-docs/pages/operations/agctl-install.md" >}} diff --git a/content/docs/kubernetes/latest/operations/debug.md b/content/docs/kubernetes/latest/operations/debug.md index 1dc623765..820a3f749 100644 --- a/content/docs/kubernetes/latest/operations/debug.md +++ b/content/docs/kubernetes/latest/operations/debug.md @@ -1,6 +1,253 @@ --- title: Debug your setup weight: 15 +description: Debug your agentgateway environment. --- -{{< reuse "agw-docs/pages/operations/debug.md" >}} +Inspect and troubleshoot agentgateway proxies through the admin endpoints and the [`agctl`]({{< link-hextra path="/operations/agctl" >}}) command-line tool. + +## Admin endpoints + +Each agentgateway pod runs an admin server on port `15000`. The admin server provides the following endpoints for inspection and debugging. + +| Endpoint | Description | +| -- | -- | +| `/config_dump` | Returns the runtime configuration that the proxy has loaded, including binds, listeners, routes, backends, workloads, services, and policies. | +| `/debug/trace` | Streams a JSON-over-SSE trace of the next request that the proxy handles. The `agctl trace` command consumes this endpoint. | +| `/logging` | Get and set the logging level at runtime. | +| `/memory` | Dump allocator and process memory statistics. | +| `/debug/pprof/profile` | Build a CPU profile by using the [pprof](https://github.com/google/pprof) profiler. Use `?seconds=N` to set the duration (1–300s, default 10s). | +| `/debug/pprof/heap` | Collect heap profiling data. | +| `/debug/tasks` | Inspect the live tokio task tree. | +| `/quitquitquit` | Trigger a graceful shutdown of the proxy. | + +To inspect the configuration that a gateway proxy has loaded and to capture per-request traces, use the [`agctl`]({{< link-hextra path="/operations/agctl" >}}) command-line tool. `agctl` resolves the proxy pod for you, opens a port-forward, and renders the admin output in formats that are easier to scan than raw JSON. + +## Before you begin + +[Install agctl]({{< link-hextra path="/operations/agctl" >}}). + +## Check the gateway, route, and policy status + +Most routing and policy issues surface in the status of the corresponding Kubernetes resource. Check these first. + +1. Verify that the agentgateway control plane and proxy pods are running. + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + kubectl get pods -n + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + kubectl get pods -n agentgateway-system + ``` + {{% /tab %}} + {{< /tabs >}} + +2. Verify the Gateway is `Accepted` and `Programmed`. + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + kubectl get gateway -A + kubectl get gateway -n -o yaml + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + kubectl get gateway agentgateway-proxy -n agentgateway-system -o yaml + ``` + {{% /tab %}} + {{< /tabs >}} + +3. Check the HTTPRoute for `Accepted` and `ResolvedRefs` conditions. + + ```sh + kubectl get httproute -A + ``` + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + kubectl get httproute -n -o yaml + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + kubectl get httproute openai -n agentgateway-system -o yaml + ``` + {{% /tab %}} + {{< /tabs >}} + + Common issues to check for: + + * The wrong backend is selected. + * The wrong parent Gateway is referenced. + * Multiple HTTPRoutes conflict by having identical matchers or by having no matchers (and so default to `/`). + +## Inspect the loaded configuration + +Sometimes a route is `Accepted` but the proxy still does not behave as expected. To see what the proxy actually loaded, dump its runtime configuration. + +1. Render a summary of the routes, backends, and policies that the gateway has loaded. + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + agctl config all gateway/ -n -o yaml + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + agctl config all gateway/agentgateway-proxy -n agentgateway-system -o yaml + ``` + {{% /tab %}} + {{< /tabs >}} + +2. Inspect the backends that the gateway is sending traffic to and their endpoint health. + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + agctl config backends gateway/ -n + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + agctl config backends gateway/agentgateway-proxy -n agentgateway-system + ``` + {{% /tab %}} + {{< /tabs >}} + + Example output: + + ``` + TYPE NAME NAMESPACE ENDPOINT HEALTH REQUESTS LATENCY + Backend openai agentgateway-system backend 1.00 1 4682.37ms + Service ext-authz backend-extauth ext-authz-7c7596b5f6-tvs28 0.70 4 0.00ms + Service httpbin backend-extauth httpbin-7dc88b5fbc-zqrfn 1.00 2 3.06ms + ``` + +For complete steps, see [Inspect agentgateway configuration]({{< link-hextra path="/operations/inspect-config" >}}). + +## Trace requests + +To see how a specific request flows through agentgateway, use `agctl trace`. The trace shows you the route that was selected, the policies that were applied, the backend that was chosen, and the response status. Tracing helps you understand why a request did or did not match a route, why a policy was or was not applied, or why a request returned an unexpected status. + +{{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} +{{% tab tabName="Replace with your own" %}} +```sh +agctl trace gateway/ -n --port -- http:/// +``` +{{% /tab %}} +{{% tab tabName="Quickstart example" %}} + +```sh +agctl trace gateway/agentgateway-proxy -n agentgateway-system --port 8080 -- http://httpbin.example.com/ +``` +{{% /tab %}} +{{< /tabs >}} + +`agctl` opens a port-forward to the proxy pod, captures the trace, sends the request, and renders the result in a text-based terminal user interface (TUI). Use `--raw` to print JSON Lines instead. + +For complete steps, see [Trace requests with agctl]({{< link-hextra path="/operations/trace-requests" >}}). + +## Enable debug logs {#debug-logs} + +Agentgateway uses the same level syntax as [`RUST_LOG`](https://docs.rs/env_logger/latest/env_logger/#enabling-logging): `error`, `warn`, `info`, `debug`, and `trace`. You can change the level at runtime through the proxy's admin endpoint. + +1. Open a port-forward to the proxy. + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + kubectl port-forward deploy/ -n 15000 & + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + kubectl port-forward deploy/agentgateway-proxy -n agentgateway-system 15000 & + ``` + {{% /tab %}} + {{< /tabs >}} + +2. Set the log level. + + ```sh + curl -X POST "http://localhost:15000/logging?level=debug" + ``` + + Example output: + + ``` + current log level is typespec_client_core::http::policies::logging=warn,hickory_server::server::server_future=off,rmcp=warn,debug + ``` + +3. Tail the proxy logs. + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + kubectl logs -n deploy/ -f + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + kubectl logs -n agentgateway-system deploy/agentgateway-proxy -f + ``` + {{% /tab %}} + {{< /tabs >}} + +You can also set fine-grained levels per module by using the same `RUST_LOG` filter syntax, such as `info,proxy::httpproxy=trace`. + +## Capture profiles + +Agentgateway includes pprof endpoints to help you investigate CPU and memory issues. + +1. Optional: If you have not already, download [Graphviz](https://graphviz.org/download/) to visualize the profiles. + +2. Open a port-forward to the proxy. + + {{< tabs tabTotal="2" items="Replace with your own, Quickstart example" >}} + {{% tab tabName="Replace with your own" %}} + ```sh + kubectl port-forward deploy/ -n 15000 & + ``` + {{% /tab %}} + {{% tab tabName="Quickstart example" %}} + ```sh + kubectl port-forward deploy/agentgateway-proxy -n agentgateway-system 15000 & + ``` + {{% /tab %}} + {{< /tabs >}} + +3. Capture a CPU profile. The default duration is 10 seconds; the example uses 30 seconds. + + ```sh + curl -o cpu.pprof "http://localhost:15000/debug/pprof/profile?seconds=30" + ``` + +4. Capture a heap profile. + + ```sh + curl -o heap.pprof http://localhost:15000/debug/pprof/heap + ``` + +5. Inspect the profiles with `go tool pprof`. + + **CPU profile** + ```sh + go tool pprof -http=: cpu.pprof + ``` + + **Heap profile** + ```sh + go tool pprof -http=: heap.pprof + ``` + + Graphviz opens on your web browser to a UI on localhost. Example: + + {{< reuse-image src="img/debug-heap-pprof.png" caption="Heap profile graph" >}} + {{< reuse-image-dark srcDark="img/debug-heap-pprof.png" caption="Heap profile graph" >}} diff --git a/content/docs/kubernetes/latest/operations/inspect-config.md b/content/docs/kubernetes/latest/operations/inspect-config.md new file mode 100644 index 000000000..8867bd848 --- /dev/null +++ b/content/docs/kubernetes/latest/operations/inspect-config.md @@ -0,0 +1,12 @@ +--- +title: Inspect agentgateway configuration +weight: 17 +description: Inspect the runtime configuration that an agentgateway proxy has loaded. +test: skip +--- + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} + +{{< reuse "agw-docs/pages/operations/inspect-config.md" >}} diff --git a/content/docs/kubernetes/latest/operations/trace-requests.md b/content/docs/kubernetes/latest/operations/trace-requests.md new file mode 100644 index 000000000..8e1f301e1 --- /dev/null +++ b/content/docs/kubernetes/latest/operations/trace-requests.md @@ -0,0 +1,12 @@ +--- +title: Trace requests with agctl +weight: 16 +description: Capture a per-request trace as an agentgateway proxy handles the request. +test: skip +--- + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} + +{{< reuse "agw-docs/pages/operations/trace-requests.md" >}} diff --git a/content/docs/kubernetes/latest/reference/_index.md b/content/docs/kubernetes/latest/reference/_index.md index a65b38486..8b5193705 100644 --- a/content/docs/kubernetes/latest/reference/_index.md +++ b/content/docs/kubernetes/latest/reference/_index.md @@ -7,3 +7,4 @@ test: skip --- Review reference docs for the API, Helm, version support,and more. + diff --git a/content/docs/kubernetes/latest/reference/agctl/_index.md b/content/docs/kubernetes/latest/reference/agctl/_index.md new file mode 100644 index 000000000..e2d3454f2 --- /dev/null +++ b/content/docs/kubernetes/latest/reference/agctl/_index.md @@ -0,0 +1,14 @@ +--- +title: agctl CLI reference +weight: 25 +description: Reference for the agctl command-line tool. +test: skip +--- + +Reference for `agctl`, the command-line tool that you use to inspect and debug agentgateway. The pages in this section are generated from the `agctl` source. + +To install `agctl`, see [Install agctl]({{< link-hextra path="/operations/agctl" >}}). + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} diff --git a/content/docs/kubernetes/latest/reference/agctl/agctl-config-all.md b/content/docs/kubernetes/latest/reference/agctl/agctl-config-all.md new file mode 100644 index 000000000..fb84cb294 --- /dev/null +++ b/content/docs/kubernetes/latest/reference/agctl/agctl-config-all.md @@ -0,0 +1,8 @@ +--- +title: agctl config all +weight: 76 +description: Reference for the `agctl config all` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-config-all.md" >}} diff --git a/content/docs/kubernetes/latest/reference/agctl/agctl-config-backends.md b/content/docs/kubernetes/latest/reference/agctl/agctl-config-backends.md new file mode 100644 index 000000000..a5be64573 --- /dev/null +++ b/content/docs/kubernetes/latest/reference/agctl/agctl-config-backends.md @@ -0,0 +1,8 @@ +--- +title: agctl config backends +weight: 90 +description: Reference for the `agctl config backends` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-config-backends.md" >}} diff --git a/content/docs/kubernetes/latest/reference/agctl/agctl-config.md b/content/docs/kubernetes/latest/reference/agctl/agctl-config.md new file mode 100644 index 000000000..f06d01bf0 --- /dev/null +++ b/content/docs/kubernetes/latest/reference/agctl/agctl-config.md @@ -0,0 +1,8 @@ +--- +title: agctl config +weight: 118 +description: Reference for the `agctl config` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-config.md" >}} diff --git a/content/docs/kubernetes/latest/reference/agctl/agctl-trace.md b/content/docs/kubernetes/latest/reference/agctl/agctl-trace.md new file mode 100644 index 000000000..2fbe069e4 --- /dev/null +++ b/content/docs/kubernetes/latest/reference/agctl/agctl-trace.md @@ -0,0 +1,8 @@ +--- +title: agctl trace +weight: 115 +description: Reference for the `agctl trace` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-trace.md" >}} diff --git a/content/docs/kubernetes/latest/reference/agctl/agctl.md b/content/docs/kubernetes/latest/reference/agctl/agctl.md new file mode 100644 index 000000000..961cdc260 --- /dev/null +++ b/content/docs/kubernetes/latest/reference/agctl/agctl.md @@ -0,0 +1,8 @@ +--- +title: agctl +weight: 10 +description: Reference for the `agctl` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl.md" >}} diff --git a/content/docs/kubernetes/latest/reference/release-notes.md b/content/docs/kubernetes/latest/reference/release-notes.md index 55683750b..2dd0f43f3 100644 --- a/content/docs/kubernetes/latest/reference/release-notes.md +++ b/content/docs/kubernetes/latest/reference/release-notes.md @@ -1,109 +1,182 @@ ---- -title: Release notes -weight: 20 -description: Review the release notes for agentgateway. -test: skip ---- - -Review the release notes for agentgateway. - -{{< callout type="info">}} -For more details, review the [GitHub release notes in the agentgateway repository](https://github.com/agentgateway/agentgateway/releases) -{{< /callout >}} - -## πŸ”₯ Breaking changes {#v11-breaking-changes} - -### MCP authentication moved to route level - - - -MCP authentication is configured at the traffic (route) level using `traffic.jwtAuthentication` with the `mcp` extension field, instead of the previous `backend.mcp.authentication` field. The route-level placement aligns MCP auth with standard JWT authentication and allows JWT claims to be used in other traffic policies such as authorization, rate limiting, and transformations. - -* **Before**: MCP authentication was configured under `backend.mcp.authentication`, targeting an {{< reuse "agw-docs/snippets/agentgateway/agentgatewaybackend.md" >}}. This previous `backend.mcp.authentication` field is deprecated but continues to work for backward compatibility. If both are set on the same route, the backend-level configuration is ignored. -* **After**: MCP authentication is configured under `traffic.jwtAuthentication` with an `mcp` field, targeting an HTTPRoute, such as in the following example. For more information, see [Set up MCP auth]({{< link-hextra path="/mcp/auth/setup/" >}}). - -```yaml -apiVersion: {{< reuse "agw-docs/snippets/trafficpolicy-apiversion.md" >}} -kind: {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} -metadata: - name: mcp-authn -spec: - targetRefs: - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: mcp - traffic: - jwtAuthentication: - mode: Strict - providers: - - issuer: http://keycloak.example.com/realms/myrealm - audiences: - - http://localhost:8080/mcp - jwks: - remote: - backendRef: - name: keycloak - kind: Service - namespace: keycloak - port: 8080 - jwksPath: /realms/master/protocol/openid-connect/certs - mcp: - provider: Keycloak - resourceMetadata: - resource: http://localhost:8080/mcp - scopesSupported: - - email - bearerMethodsSupported: - - header -``` - -## 🌟 New features {#v11-new-features} - -### Network authorization - -A new `networkAuthorization` field in the `frontend` policy section enables Layer 4 network authorization based on source IP, port, and mTLS client identity. You can enforce policies for non-HTTP traffic and layer L4+L7 controls. For more information, see [Policies]({{< link-hextra path="/about/policies/" >}}). - -### Authorization require rules - -Authorization policies now support `Require` as an action in addition to `Allow` and `Deny`. All `Require` rules must match for the request to proceed, providing clearer semantics than double-negative deny rules. For more information, see [Policies]({{< link-hextra path="/about/policies/" >}}). - -### MCP improvements - -- **Stateless sessions**: MCP upstreams can now use stateless sessions. For more information, see [Stateful MCP]({{< link-hextra path="/mcp/session/" >}}). -- **Explicit service reference lists**: MCP backends can specify targets with explicit service references. For more information, see [Static MCP]({{< link-hextra path="/mcp/static-mcp/" >}}). -- **Tool payloads in CEL context**: Tool names and payloads are available in logging CEL expressions. - -### LLM gateway enhancements - -- **Path prefixes**: LLM providers now support path prefixes for custom API base paths. -- **Azure default authentication**: Azure OpenAI providers can use platform-default authentication. For more information, see [Azure OpenAI]({{< link-hextra path="/llm/providers/azure/" >}}). -- **Vertex region optional**: Vertex AI region configuration is now optional with a global default. For more information, see [Vertex AI]({{< link-hextra path="/llm/providers/vertex/" >}}). - -### Gateway and routing improvements - -- **Automatic protocol detection**: A new `auto` bind protocol auto-detects TLS vs HTTP connections. -- **Service SANs for upstream TLS**: Upstream TLS now respects Subject Alternative Names from Kubernetes Services. For more information, see [BackendTLS]({{< link-hextra path="/security/backendtls/" >}}). -- **TLSRoute v1 status**: Status is now written using the `TLSRoute v1` API version. -- **CEL hash functions**: New `sha1.encode`, `sha256.encode`, and `md5.encode` functions are available in CEL expressions. - -## πŸ—‘οΈ Deprecated or removed features - -### MCP authentication on backend AgentgatewayPolicy - -As described in the breaking changes section, MCP authentication is now configured at the route level using `traffic.jwtAuthentication` with the `mcp` extension field, instead of the previous `backend.mcp.authentication` field. - -The `backend.mcp.authentication` field on the AgentgatewayPolicy resource is deprecated and will be removed in a future release. - -### MCP policy on AgentgatewayBackend - - - -Previously, AgentgatewayBackend resources had fields for `spec.mcp.targets.static.policies.mcp.{authentication,authorization}`. - -These fields were not intended to be set, and had no impact on the behavior of the proxy. - -As such, these fields are now removed. - -If you previously set these fields which had no behavioral impact and were ignored, the configuration now fails to be applied. - -Instead, use the `jwtAuthentication.mcp` field on the AgentgatewayPolicy resource, which ensures authentication runs before other policies such as transformation and rate limiting. +--- +title: Release notes +weight: 20 +description: Review the release notes for agentgateway. +test: skip +--- + +Review the release notes for agentgateway. + +{{< callout type="info">}} +For more details, review the [GitHub release notes in the agentgateway repository](https://github.com/agentgateway/agentgateway/releases) +{{< /callout >}} + +## πŸ”₯ Breaking changes {#v12-breaking-changes} + +### xDS Helm configuration changed + +The controller Helm value for xDS TLS changed from a boolean to an explicit transport mode. + +Before: + +```yaml +controller: + xds: + tls: + enabled: true +``` + +Now: + +```yaml +controller: + xds: + mode: tls +``` + +Supported values are `plaintext`, `tls`, and `either`. The chart now defaults to `tls` to enable TLS encryption for the controller. If you have automation that sets `controller.xds.tls.enabled`, update it to use `controller.xds.mode`. For more information, see [TLS encryption]({{< link-hextra path="/install/tls/" >}}). + +## 🌟 New features {#v12-new-features} + +### Conditional policy execution + +Policies can now be selected conditionally using CEL expressions. Conditional execution is supported for external auth, transformations, rate limiting, external processing, and direct responses. The first matching policy is applied, with an optional final fallback entry. For more information, see [Conditional policies]({{< link-hextra path="/about/policies/conditional-policies/" >}}). + +```yaml +traffic: + transformation: + conditional: + - condition: request.headers["x-user"] == "admin" + policy: + request: + set: + - name: x-role + value: admin + - policy: + request: + set: + - name: x-role + value: user +``` + +### Automatic xDS TLS management + +Kubernetes installs can now run xDS over TLS without requiring users to pre-create serving certificates. The controller creates and rotates a local CA and short-lived serving certificates, while still supporting user-provided certificates. For more information, see [TLS encryption]({{< link-hextra path="/install/tls/" >}}). + +### Route delegation + +Agentgateway now supports Gateway API route delegation, allowing parent routes to delegate portions of their routing tree to child routes. Platform teams can own shared parent routes while application teams manage delegated route fragments. For more information, see [Route delegation]({{< link-hextra path="/traffic-management/route-delegation/overview/" >}}). + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: parent + namespace: default +spec: + rules: + - matches: + - path: + type: PathPrefix + value: /anything/team1 + backendRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: "*" + namespace: team1 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: child-team1-foo + namespace: team1 +spec: + rules: + - matches: + - path: + type: PathPrefix + value: /anything/team1/foo + backendRefs: + - name: httpbin + port: 8000 +``` + +### `agctl` CLI debugging tool + +A new experimental `agctl` command-line tool for inspecting and debugging agentgateway is now available. To install `agctl`, see [Install agctl]({{< link-hextra path="/operations/agctl/" >}}). +- `agctl config` renders the runtime configuration that an agentgateway proxy has loaded, including binds, listeners, routes, backends, workloads, and policies, as a structured table, JSON, or YAML. `agctl config backends` shows per-backend health, request counts, and latency. For more information, see [Inspect agentgateway configuration]({{< link-hextra path="/operations/inspect-config/" >}}). +- `agctl trace` streams a step-by-step trace of how the proxy processes the next request, showing the matched route, applied policies, chosen backend, and response status. For more information, see [Trace requests with agctl]({{< link-hextra path="/operations/trace-requests/" >}}). + +For a complete command reference, see [agctl CLI reference]({{< link-hextra path="/reference/agctl/" >}}). + +### Policy targets + +The `AgentgatewayPolicy` resource can now target resources by label selector in addition to explicit target references. This configuration makes it easier to apply shared policy configuration across groups of Gateways, Routes, Services, or Backends. + +Additionally, the `AgentgatewayPolicy` resource can now target `ListenerSet` and `InferencePool` resources. For more information, see [Targeting and merging]({{< link-hextra path="/about/policies/target-merge/" >}}). + +### PROXY protocol support + +Kubernetes and proxy listeners now support downstream PROXY protocol handling, including strict and optional modes and PROXY protocol v1/v2 selection. For more information, see [Listener overview]({{< link-hextra path="/setup/listeners/overview/" >}}). + +### Locality load balancing and failover + +The data plane now supports locality-aware load balancing and failover, improving traffic placement for multi-zone and multi-region deployments. For more information, see [Locality-aware routing]({{< link-hextra path="/traffic-management/locality-aware-routing/" >}}). + +### LLM gateway enhancements + +- **Azure provider**: A new Azure provider supports both Azure OpenAI and Azure AI Foundry style resources. For more information, see [Azure]({{< link-hextra path="/llm/providers/azure/" >}}). +- **Copilot support**: Added Copilot authentication and LLM provider support. +- **Gemini Responses API**: Responses API requests can now be routed to Gemini. For more information, see [Gemini]({{< link-hextra path="/llm/providers/gemini/" >}}). +- **Path prefixes**: Custom path prefixes now work across all LLM providers, including Gemini, Vertex, Bedrock, and Azure. +- **OpenAI compatibility**: OpenAI chat completion requests now normalize `max_tokens` to `max_completion_tokens`. +- **Azure Content Safety guardrails**: Prompt and response guardrails can now use Azure AI Content Safety. For more information, see [Guardrails]({{< link-hextra path="/llm/guardrails/overview/" >}}). +- **Bedrock guardrails masking**: Bedrock guardrails now support masking. For more information, see [AWS Bedrock Guardrails]({{< link-hextra path="/llm/guardrails/bedrock-guardrails/" >}}). + +### MCP improvements + +- **Session TTL**: MCP sessions can now be configured with an idle TTL. For more information, see [MCP sessions]({{< link-hextra path="/mcp/session/" >}}). +- **Stateless MCP**: Stateless MCP initialization and shutdown behavior is improved. +- **List resources with multiplexing**: `ListResourcesRequest` now works with multiplexed MCP targets. + +### Authentication and authorization + +- **Backend external auth**: External auth can now run as a backend policy after backend selection. For more information, see [External auth]({{< link-hextra path="/security/extauth/" >}}). +- **Auth credential locations**: JWT, basic auth, API key, and backend auth can now override where credentials are read from or inserted, including headers, query parameters, and cookies. For more information, see [JWT auth]({{< link-hextra path="/security/jwt/" >}}) and [API key auth]({{< link-hextra path="/security/apikey/" >}}). +- **Explicit GCP credentials**: GCP backend auth can now use explicit Secret-backed credentials. + +### Traffic, TLS, and networking + +- **Post-quantum TLS**: TLS configuration now supports post-quantum key exchange groups, including `X25519_MLKEM768`. For more information, see [Additional TLS settings]({{< link-hextra path="/setup/listeners/tls-settings/" >}}). +- **Istio workload TLS**: Listeners can use Istio workload certificates for simple TLS or mutual TLS. For more information, see [mTLS]({{< link-hextra path="/setup/listeners/mtls/" >}}). +- **HBONE gateway tunnel protocol**: Controller support was added for the `HBONE_GATEWAY` tunnel protocol. +- **Unix Domain Socket backends**: Kubernetes static backends can now target Unix Domain Sockets. +- **Max connection duration**: HTTP listeners can now enforce a maximum connection duration. +- **HTTP/2 pooling**: HTTP/2 connection pooling is improved to avoid the single-connection bottleneck. + +### Operations + +- Agentgateway's memory allocator performance is improved, resulting in increased runtime performance and decreased memory utilization. +- A new `/debug/pprof/heap` endpoint is available to get a `pprof` snapshot of current and historical allocations. + +### Telemetry + +- **Custom Prometheus labels**: `AgentgatewayPolicy` can add custom Prometheus metric labels using CEL expressions. +- **OpenTelemetry environment variables**: The OTEL configuration now respects standard environment variables. For more information, see [OTel stack]({{< link-hextra path="/observability/otel-stack/" >}}). + +## πŸͺ² Notable fixes {#v12-fixes} + +- Fixed A2A policy matching for agents hosted under sub-paths. +- Fixed A2A and MCP handling of `X-Forwarded-Proto`. +- Fixed service parents and arbitrary parents in route delegation. +- Fixed policies that target missing or non-existent backends from silently attaching to the gateway. +- Fixed JWKS stale fetches, startup fetch behavior, cache cleanup, and orphan cleanup. +- Fixed CEL property parsing after bracket accessors. +- Fixed CEL `response.body` access when upstream responses are compressed. +- Fixed request body buffering when CEL expressions do not need the body. +- Fixed `Host` and `:authority` alignment after header mutation. +- Fixed stripping of hop-by-hop connection headers and encoding headers for more consistent behavior. +- Improved xDS error semantics for regex, CEL, and rate-limit-service failures. +- Improved Gateway status updates to avoid unnecessary churn while preserving transition times. +- Fixed invalid htpasswd entries to fail gracefully instead of breaking basic auth handling. +- Fixed active stream accounting in the connection pool when debug assertions are disabled. diff --git a/content/docs/kubernetes/latest/security/apikey.md b/content/docs/kubernetes/latest/security/apikey.md new file mode 100644 index 000000000..5427a5a2d --- /dev/null +++ b/content/docs/kubernetes/latest/security/apikey.md @@ -0,0 +1,7 @@ +--- +title: API key auth +weight: 10 +description: Set up API key auth to secure requests to an LLM, MCP server, or agent. +--- + +{{< reuse "agw-docs/pages/security/apikey-auth.md" >}} \ No newline at end of file diff --git a/content/docs/kubernetes/latest/traffic-management/extproc.md b/content/docs/kubernetes/latest/traffic-management/extproc.md index bb1253eb5..4e5ad4603 100644 --- a/content/docs/kubernetes/latest/traffic-management/extproc.md +++ b/content/docs/kubernetes/latest/traffic-management/extproc.md @@ -238,6 +238,10 @@ EOF ... ``` +## Conditional execution + +To send only certain requests through external processing, use the `conditional` field on your `extProc` policy. For example, you can route LLM chat traffic through a content filter and bypass the processor for every other request. For details, see [Conditional policies]({{< link-hextra path="/about/policies/conditional-policies" >}}). + ## Cleanup {{< reuse "agw-docs/snippets/cleanup.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/header-control/_index.md b/content/docs/kubernetes/latest/traffic-management/header-control/_index.md index 09342c555..2c805e9eb 100644 --- a/content/docs/kubernetes/latest/traffic-management/header-control/_index.md +++ b/content/docs/kubernetes/latest/traffic-management/header-control/_index.md @@ -6,4 +6,5 @@ test: skip Modify the headers of HTTP requests and responses. +## Guides diff --git a/content/docs/kubernetes/latest/traffic-management/locality-aware-routing.md b/content/docs/kubernetes/latest/traffic-management/locality-aware-routing.md new file mode 100644 index 000000000..45a5a802f --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/locality-aware-routing.md @@ -0,0 +1,13 @@ +--- +title: Locality-aware routing +weight: 50 +description: Reduce cross-zone traffic costs and latency with topology-aware routing, and fail over to other localities when local endpoints are unavailable. +test: + locality-aware-routing: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/traffic-management/locality-aware-routing.md + path: locality-aware-routing +--- + +{{< reuse "agw-docs/pages/traffic-management/locality-aware-routing.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/_index.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/_index.md new file mode 100644 index 000000000..331a0f1cf --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/_index.md @@ -0,0 +1,8 @@ +--- +title: Route delegation +weight: 20 +description: Manage routing rules more effectively by using multiple connected HTTPRoute resources. +test: skip +--- + +Manage routing rules more effectively by using multiple connected HTTPRoute resources. diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/basic.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/basic.md new file mode 100644 index 000000000..55837179f --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/basic.md @@ -0,0 +1,17 @@ +--- +title: Basic example +weight: 10 +description: Set up basic route delegation between a parent HTTPRoute and two child HTTPRoutes. +test: + basic: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/basic.md + path: route-delegation-prereq + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/basic.md + path: basic +--- + +{{< reuse "agw-docs/pages/traffic-management/route-delegation/basic.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/header-query.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/header-query.md new file mode 100644 index 000000000..3c5cd3b91 --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/header-query.md @@ -0,0 +1,17 @@ +--- +title: Header and query match +weight: 40 +description: Use header and query matchers in a route delegation setup. +test: + header-query: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/header-query.md + path: route-delegation-prereq + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/header-query.md + path: header-query +--- + +{{< reuse "agw-docs/pages/traffic-management/route-delegation/header-query.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/_index.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/_index.md new file mode 100644 index 000000000..0f260a499 --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/_index.md @@ -0,0 +1,13 @@ +--- +title: Policy inheritance and overrides +weight: 50 +description: Learn how policies are inherited and overridden along the route delegation chain. +test: skip +--- + +Review how policies are inherited and overridden along the route delegation chain. Choose a guide based on the type of policy that you want to apply. + +{{< cards >}} + {{< card link="native-policies" title="Native Gateway API policies" >}} + {{< card link="trafficpolicies" title="AgentgatewayPolicy resources" >}} +{{< /cards >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/native-policies.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/native-policies.md new file mode 100644 index 000000000..ec4ac4e7e --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/native-policies.md @@ -0,0 +1,17 @@ +--- +title: Native Gateway API policies +weight: 10 +description: Learn how Kubernetes Gateway API policies, such as request timeouts, are inherited and overridden along the route delegation chain. +test: + native-policies: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/native-policies.md + path: route-delegation-prereq + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/native-policies.md + path: native-policies +--- + +{{< reuse "agw-docs/pages/traffic-management/route-delegation/inheritance/native-policies.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/trafficpolicies.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/trafficpolicies.md new file mode 100644 index 000000000..d56175c90 --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/trafficpolicies.md @@ -0,0 +1,17 @@ +--- +title: AgentgatewayPolicy resources +weight: 20 +description: Learn how policies in `AgentgatewayPolicy` resources are inherited and overridden along the route delegation chain. +test: + trafficpolicies: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/trafficpolicies.md + path: route-delegation-prereq + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/inheritance/trafficpolicies.md + path: trafficpolicies +--- + +{{< reuse "agw-docs/pages/traffic-management/route-delegation/inheritance/trafficpolicies.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/label.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/label.md new file mode 100644 index 000000000..6659cc796 --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/label.md @@ -0,0 +1,17 @@ +--- +title: Delegation via labels +weight: 20 +description: Use labels to delegate traffic to child HTTPRoutes with the `=` syntax. +test: + label: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/label.md + path: route-delegation-prereq + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/label.md + path: label +--- + +{{< reuse "agw-docs/pages/traffic-management/route-delegation/label.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/multi-level-delegation.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/multi-level-delegation.md new file mode 100644 index 000000000..dc9138f18 --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/multi-level-delegation.md @@ -0,0 +1,17 @@ +--- +title: Multi-level delegation +weight: 30 +description: Create a 3-level route delegation hierarchy with a parent, child, and grandchild HTTPRoute. +test: + multi-level: + - file: content/docs/kubernetes/latest/quickstart/install.md + path: experimental + - file: content/docs/kubernetes/latest/setup/gateway.md + path: all + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/multi-level-delegation.md + path: route-delegation-prereq + - file: content/docs/kubernetes/latest/traffic-management/route-delegation/multi-level-delegation.md + path: multi-level +--- + +{{< reuse "agw-docs/pages/traffic-management/route-delegation/multi-level-delegation.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/route-delegation/overview.md b/content/docs/kubernetes/latest/traffic-management/route-delegation/overview.md new file mode 100644 index 000000000..261634301 --- /dev/null +++ b/content/docs/kubernetes/latest/traffic-management/route-delegation/overview.md @@ -0,0 +1,8 @@ +--- +title: Route delegation overview +weight: 5 +description: Learn what route delegation is, how it works in agentgateway, and when to use it. +test: skip +--- + +{{< reuse "agw-docs/pages/traffic-management/route-delegation/overview.md" >}} diff --git a/content/docs/kubernetes/latest/traffic-management/transformations/_index.md b/content/docs/kubernetes/latest/traffic-management/transformations/_index.md index 7e5e68d8e..e290e0463 100644 --- a/content/docs/kubernetes/latest/traffic-management/transformations/_index.md +++ b/content/docs/kubernetes/latest/traffic-management/transformations/_index.md @@ -6,3 +6,6 @@ test: skip Mutate and transform requests and responses before forwarding them to the destination. +## Conditional execution + +To run a transformation only when a CEL expression matches, use the `conditional` field on your `transformation` policy. For example, you can transform internal traffic only and leave external traffic untouched. For details, see [Conditional policies]({{< link-hextra path="/about/policies/conditional-policies" >}}). diff --git a/content/docs/standalone/latest/_index.md b/content/docs/standalone/latest/_index.md index 3fa032e1a..dedd8292f 100644 --- a/content/docs/standalone/latest/_index.md +++ b/content/docs/standalone/latest/_index.md @@ -1,7 +1,7 @@ --- -linkTitle: "Latest" -title: Latest -description: Welcome to the latest standalone docs for agentgateway. +linkTitle: "Version 1.2.x" +title: Version 1.2.x +description: Use agentgateway as a standalone binary. test: skip --- diff --git a/content/docs/standalone/latest/configuration/policies/_index.md b/content/docs/standalone/latest/configuration/policies/_index.md new file mode 100644 index 000000000..7eaadb099 --- /dev/null +++ b/content/docs/standalone/latest/configuration/policies/_index.md @@ -0,0 +1,9 @@ +--- +title: Policies +weight: 11 +description: Apply policies to shape traffic that flows through agentgateway. +--- + +{{< gloss "Policy" >}}Policies{{< /gloss >}} are a powerful feature of agentgateway that allow you to manipulate traffic as it flows through your gateway. +Policies can be used to manipulate traffic, configure observability, enforce rich security rules, and more. + diff --git a/content/docs/standalone/latest/configuration/policies.md b/content/docs/standalone/latest/configuration/policies/attachment.md similarity index 81% rename from content/docs/standalone/latest/configuration/policies.md rename to content/docs/standalone/latest/configuration/policies/attachment.md index 02552a22f..98fd693aa 100644 --- a/content/docs/standalone/latest/configuration/policies.md +++ b/content/docs/standalone/latest/configuration/policies/attachment.md @@ -1,25 +1,21 @@ --- -title: Policies -weight: 11 -description: +title: Attachment points +weight: 10 +description: Learn how to attach policies to different resources in agentgateway. +test: skip --- -{{< gloss "Policy" >}}Policies{{< /gloss >}} are a powerful feature of agentgateway that allow you to manipulate traffic as it flows through your gateway. -Policies can be used to manipulate traffic, configurable observability, enforce rich security rules, and more. - -## Attachment points - You can attach policies at the {{< gloss "Listener" >}}listener{{< /gloss >}}, {{< gloss "Route" >}}route{{< /gloss >}}, or {{< gloss "Backend" >}}backend{{< /gloss >}} level to provide fine-grained control over traffic. +## Phases + Policies that are attached at multiple levels are applied at all levels. |Section|Available Policies|Phase| |-|-|-| |Listener|{{< gloss "JWT (JSON Web Token)" >}}JWT{{< /gloss >}}, External Authorization, {{< gloss "ExtProc (External Processing)" >}}External Processing{{< /gloss >}}, {{< gloss "Transformation" >}}Transformation{{< /gloss >}}, Basic {{< gloss "Authentication (AuthN)" >}}Authentication{{< /gloss >}}, {{< gloss "API Key" >}}API Key{{< /gloss >}} authentication|Runs before route selection| |Route|All Policies|Runs after route selection, before backend selection| -|Backend|Backend TLS, Backend Authentication, Backend HTTP, Backend TCP, AI/LLM, MCP Authorization, MCP Authentication, Header modification|Runs after backend selection| - -{{< reuse "agw-docs/pages/agentgateway/about/processing-order.md" >}} +|Backend|Backend TLS, Backend Authentication, Backend HTTP, Backend TCP, AI/LLM, MCP Authorization, MCP Authentication, External Authorization, Header modification|Runs after backend selection| ## Example policy configuration diff --git a/content/docs/standalone/latest/configuration/policies/conditional-policies.md b/content/docs/standalone/latest/configuration/policies/conditional-policies.md new file mode 100644 index 000000000..50fdf563e --- /dev/null +++ b/content/docs/standalone/latest/configuration/policies/conditional-policies.md @@ -0,0 +1,8 @@ +--- +title: Conditional policies +weight: 15 +description: Run different variants of a policy on the same route based on a CEL expression evaluated against the request. +test: skip +--- + +{{< reuse "agw-docs/pages/conditional-policies.md" >}} \ No newline at end of file diff --git a/content/docs/standalone/latest/configuration/resiliency/rate-limits.md b/content/docs/standalone/latest/configuration/resiliency/rate-limits.md index ebad5b5c6..494f99a60 100644 --- a/content/docs/standalone/latest/configuration/resiliency/rate-limits.md +++ b/content/docs/standalone/latest/configuration/resiliency/rate-limits.md @@ -181,3 +181,7 @@ remoteRateLimit: | `policies.backendTLS` | TLS settings for the connection to the rate limit service. Use `root` to specify a CA cert, `insecure: true` to skip certificate verification (not recommended for production). | | `policies.tcp.connectTimeout` | Connection timeout specified as `secs` and `nanos`. | | `policies.http.requestTimeout` | Request-level timeout as a duration string (for example, `"5s"`). Use for HTTP-based rate limit service connections. | + +## Conditional execution + +To apply different rate limits based on the request, use the `conditional` field. For example, you can apply stricter limits on writes than on reads. For details, see [Conditional policies]({{< link-hextra path="/configuration/policies/conditional-policies" >}}). diff --git a/content/docs/standalone/latest/configuration/security/external-authz.md b/content/docs/standalone/latest/configuration/security/external-authz.md index 68728ea1c..b7d355aea 100644 --- a/content/docs/standalone/latest/configuration/security/external-authz.md +++ b/content/docs/standalone/latest/configuration/security/external-authz.md @@ -3,7 +3,7 @@ title: External authorization weight: 20 --- -Attaches to: {{< badge content="Listener" path="/configuration/listeners/">}} {{< badge content="Route" path="/configuration/routes/">}} +Attaches to: {{< badge content="Listener" path="/configuration/listeners/">}} {{< badge content="Route" path="/configuration/routes/">}} {{< badge content="Backend" path="/configuration/backends/">}} When {{< gloss "Authorization (AuthZ)" >}}authorization{{< /gloss >}} decisions need to be made out-of-process, use an external authorization policy. This policy has agentgateway send the request to an external server, such as [Open Policy Agent](https://www.openpolicyagent.org/docs/envoy) which decides whether the request is allowed or denied. @@ -98,3 +98,25 @@ extAuthz: | `policies.backendAuth` | Credentials to authenticate to the authorization service. Supports `key` (API key from file or inline), `gcp`, `aws`, and `azure` auth. | | `policies.http.requestTimeout` | Request-level timeout as a duration string (for example, `"5s"`). | | `policies.tcp.connectTimeout` | Connection timeout specified as `secs` and `nanos`. | + +## Backend-level external authorization + +You can also attach an `extAuthz` policy directly to a backend. Backend-level external authorization runs after agentgateway selects the backend, so the policy applies even when a route load-balances or fails over across multiple backends. Attach at the backend level when the authorization service shapes the outgoing request, for example by inserting a token, rather than only deciding whether the incoming request is allowed. + +```yaml +binds: +- port: 3000 + listeners: + - routes: + - backends: + - host: localhost:8080 + policies: + extAuthz: + host: localhost:9000 + protocol: + grpc: {} +``` + +## Conditional execution + +To choose between multiple external authorization servers based on the request, use the `conditional` field. For example, you can send admin paths to a stricter authorization server and route every other request to a standard one. For details, see [Conditional policies]({{< link-hextra path="/configuration/policies/conditional-policies" >}}). diff --git a/content/docs/standalone/latest/configuration/traffic-management/direct-response.md b/content/docs/standalone/latest/configuration/traffic-management/direct-response.md index f582733b6..9f797cf85 100644 --- a/content/docs/standalone/latest/configuration/traffic-management/direct-response.md +++ b/content/docs/standalone/latest/configuration/traffic-management/direct-response.md @@ -15,3 +15,7 @@ directResponse: body: "Not found!" status: 404 ``` + +## Conditional execution + +To return a direct response only when a CEL expression matches, use the `conditional` field. For example, you can return `410 Gone` on deprecated paths and let every other request reach the backend. For details, see [Conditional policies]({{< link-hextra path="/configuration/policies/conditional-policies" >}}). diff --git a/content/docs/standalone/latest/configuration/traffic-management/extproc.md b/content/docs/standalone/latest/configuration/traffic-management/extproc.md index 8470ba9bf..b9a6062a8 100644 --- a/content/docs/standalone/latest/configuration/traffic-management/extproc.md +++ b/content/docs/standalone/latest/configuration/traffic-management/extproc.md @@ -89,3 +89,8 @@ binds: host: "extproc.com:9000" failureMode: failClosed ``` + +## Conditional execution + +To send only certain requests through external processing, use the `conditional` field. For example, you can route LLM chat traffic through a content filter and bypass the processor for every other request. For details, see [Conditional policies]({{< link-hextra path="/configuration/policies/conditional-policies" >}}). + diff --git a/content/docs/standalone/latest/configuration/traffic-management/route-delegation.md b/content/docs/standalone/latest/configuration/traffic-management/route-delegation.md new file mode 100644 index 000000000..efe02bd73 --- /dev/null +++ b/content/docs/standalone/latest/configuration/traffic-management/route-delegation.md @@ -0,0 +1,340 @@ +--- +title: Route delegation +weight: 15 +description: Delegate routing decisions to route groups so that different teams can independently manage their own routes. +--- + +Delegate routing decisions from a parent route to a set of child routes defined in a route group. Route delegation lets you break up large routing configurations into smaller, independently managed pieces. + +## About + +As your gateway manages traffic for more routes, managing all routing rules in a single configuration becomes difficult. Route delegation lets you split routing configurations so that different teams can own their routes independently. + +In standalone mode, route delegation uses **route groups**. A parent route references a route group as its backend, and the route group contains child routes that handle more specific paths within the parent's prefix. + +| Element | Description | +|---|---| +| **Parent route** | A route defined on a listener. Instead of routing directly to a backend, it references a `routeGroup` in its `backends` list. The parent must use a `pathPrefix` matcher. | +| **Route group** | Defined at the top level under `routeGroups`. Contains a `name` and a list of child `routes`. | +| **Child route** | A route inside a route group. Uses the same matching logic as regular routes (path, headers, query parameters). The child's path must fall within the parent's prefix to be reachable. | + +### Example request flow + +``` +Request: /anything/team1/foo + | + v + Parent Route (matches /anything/team1) + | + | delegates to routeGroup: team1-routes + v + Route Group "team1-routes" + | + | selects best matching child route + v + Child Route "child-foo" (matches /anything/team1/foo) + | + v + Backend: team1-foo.example.com:8080 +``` + +### More details + +Review more details about how route delegation works in standalone mode. + +| Area | Description | +|---|---| +| Parent path matcher | A parent route that delegates to a route group must use a `pathPrefix` matcher. | +| Child path scope | Child routes must match a path that falls within the parent's prefix. For example, if the parent matches `/api`, a child must match a path starting with `/api`. | +| Cyclic delegation | Agentgateway does not allow cyclic delegation. If route group A delegates to B, and B delegates back to A, agentgateway detects the cycle at runtime and returns an error. | +| Missing route group | If a route references a `routeGroup` that does not exist, the route is replaced with a 500 HTTP response. | + +## Before you begin + +{{< reuse "agw-docs/snippets/prereq-agentgateway.md" >}} + +## Basic delegation + +Delegate traffic from a parent route to a route group with two child routes. + +In this example, a parent route matches the `/anything/team1` prefix and delegates to a route group called `team1-routes`. The route group contains two child routes: `child-foo` matches `/anything/team1/foo` and `child-bar` matches `/anything/team1/bar`. + +1. Create the configuration file. + + ```sh + cat > config.yaml <<'EOF' + # yaml-language-server: $schema=https://agentgateway.dev/schema/config + binds: + - port: 3000 + listeners: + - protocol: HTTP + routes: + - name: parent-team1 + matches: + - path: + pathPrefix: /anything/team1 + backends: + - routeGroup: team1-routes + + routeGroups: + - name: team1-routes + routes: + - name: child-foo + matches: + - path: + pathPrefix: /anything/team1/foo + backends: + - host: team1-foo.example.com:8080 + - name: child-bar + matches: + - path: + pathPrefix: /anything/team1/bar + backends: + - host: team1-bar.example.com:8080 + EOF + ``` + +2. Run the gateway. + + ```sh + agentgateway -f config.yaml + ``` + +3. Test the routes. + + ```sh + # Matches parent -> delegates to team1-routes -> matches child-foo + curl -i 127.0.0.1:3000/anything/team1/foo + + # Matches parent -> delegates to team1-routes -> matches child-bar + curl -i 127.0.0.1:3000/anything/team1/bar + + # Matches parent prefix, but no child route matches -> 404 + curl -i 127.0.0.1:3000/anything/team1/other + + # Does not match parent prefix -> 404 + curl -i 127.0.0.1:3000/other + ``` + +## Header and query matching + +Parent routes can include header and query parameter matchers that control which requests are delegated. Child routes can independently define their own matchers. A request must satisfy both the parent's and the child's matchers to reach a backend. + +In this example, a parent route matches `/anything/team1` only when the `x-team` header and `env` query parameter are present. The route group has two child routes: + +* `child-foo` adds its own header matcher (`x-role`) beyond what the parent requires. A request must include both the parent's and child's matchers to reach the backend. +* `child-bar` matches on path only, with no additional header or query parameter matchers. Any request that the parent delegates is routed if the path matches. + +1. Create the configuration file. + + ```sh + cat > config.yaml <<'EOF' + # yaml-language-server: $schema=https://agentgateway.dev/schema/config + binds: + - port: 3000 + listeners: + - protocol: HTTP + routes: + - name: parent-team1 + matches: + - path: + pathPrefix: /anything/team1 + headers: + - name: x-team + value: + exact: team1 + query: + - name: env + value: + exact: prod + backends: + - routeGroup: team1-routes + + routeGroups: + - name: team1-routes + routes: + - name: child-foo + matches: + - path: + pathPrefix: /anything/team1/foo + headers: + - name: x-role + value: + exact: admin + backends: + - host: team1-foo.example.com:8080 + - name: child-bar + matches: + - path: + pathPrefix: /anything/team1/bar + backends: + - host: team1-bar.example.com:8080 + EOF + ``` + +2. Run the gateway. + + ```sh + agentgateway -f config.yaml + ``` + +3. Test the routes. + + ```sh + # child-foo: parent matchers + child's x-role header -> 200 + curl -i "127.0.0.1:3000/anything/team1/foo?env=prod" \ + -H "x-team: team1" -H "x-role: admin" + + # child-foo: parent matchers only, missing child's x-role -> 404 + curl -i "127.0.0.1:3000/anything/team1/foo?env=prod" \ + -H "x-team: team1" + + # child-bar: parent matchers, child matches on path only -> 200 + curl -i "127.0.0.1:3000/anything/team1/bar?env=prod" \ + -H "x-team: team1" + + # child-bar: missing parent matchers, not delegated -> 404 + curl -i 127.0.0.1:3000/anything/team1/bar + ``` + +## Multi-level delegation + +Child routes inside a route group can delegate to other route groups, creating a multi-level delegation hierarchy. Agentgateway detects cycles at runtime and returns an error if a delegation chain loops back to a previously visited route group. + +In this example, a parent route delegates `/api` to a route group. One child handles `/api/users` directly, while another child delegates `/api/orders` to a second route group with more specific routes. + +1. Create the configuration file. + + ```sh + cat > config.yaml <<'EOF' + # yaml-language-server: $schema=https://agentgateway.dev/schema/config + binds: + - port: 3000 + listeners: + - protocol: HTTP + routes: + - name: parent-api + matches: + - path: + pathPrefix: /api + backends: + - routeGroup: api-routes + + routeGroups: + - name: api-routes + routes: + - name: child-users + matches: + - path: + pathPrefix: /api/users + backends: + - host: users-service.example.com:8080 + - name: child-orders + matches: + - path: + pathPrefix: /api/orders + backends: + - routeGroup: orders-routes + - name: orders-routes + routes: + - name: grandchild-list + matches: + - path: + pathPrefix: /api/orders/list + backends: + - host: orders-list.example.com:8080 + - name: grandchild-detail + matches: + - path: + pathPrefix: /api/orders/detail + backends: + - host: orders-detail.example.com:8080 + EOF + ``` + +2. Run the gateway. + + ```sh + agentgateway -f config.yaml + ``` + +3. Test the routes. + + ```sh + # Parent -> api-routes -> child-users (direct backend) + curl -i 127.0.0.1:3000/api/users + + # Parent -> api-routes -> child-orders -> orders-routes -> grandchild-list + curl -i 127.0.0.1:3000/api/orders/list + + # Parent -> api-routes -> child-orders -> orders-routes -> grandchild-detail + curl -i 127.0.0.1:3000/api/orders/detail + + # Matches child-orders prefix, but no grandchild matches -> 404 + curl -i 127.0.0.1:3000/api/orders/other + ``` + +## Policy inheritance + +Policies defined on a parent route are inherited by child routes in the delegation chain. If a child route defines the same type of policy, the child's policy takes precedence. + +In this example, a parent route sets a `requestHeaderModifier` policy that adds an `x-parent` header to all requests. One child route inherits this policy, while the other overrides it with its own `requestHeaderModifier` that adds a different header instead. + +1. Create the configuration file. + + ```sh + cat > config.yaml <<'EOF' + # yaml-language-server: $schema=https://agentgateway.dev/schema/config + binds: + - port: 3000 + listeners: + - protocol: HTTP + routes: + - name: parent-team1 + matches: + - path: + pathPrefix: /anything/team1 + policies: + requestHeaderModifier: + add: + x-parent: from-parent + backends: + - routeGroup: team1-routes + + routeGroups: + - name: team1-routes + routes: + - name: child-inherits + matches: + - path: + pathPrefix: /anything/team1/foo + backends: + - host: team1-foo.example.com:8080 + - name: child-overrides + matches: + - path: + pathPrefix: /anything/team1/bar + policies: + requestHeaderModifier: + add: + x-child: from-child + backends: + - host: team1-bar.example.com:8080 + EOF + ``` + +2. Run the gateway. + + ```sh + agentgateway -f config.yaml + ``` + +3. Test the routes. + + ```sh + # child-inherits: receives x-parent header from parent policy + curl -i 127.0.0.1:3000/anything/team1/foo + + # child-overrides: receives x-child header; parent's requestHeaderModifier is overridden + curl -i 127.0.0.1:3000/anything/team1/bar + ``` diff --git a/content/docs/standalone/latest/configuration/traffic-management/transformations.md b/content/docs/standalone/latest/configuration/traffic-management/transformations.md index f81d8d4b2..5230da7fa 100644 --- a/content/docs/standalone/latest/configuration/traffic-management/transformations.md +++ b/content/docs/standalone/latest/configuration/traffic-management/transformations.md @@ -58,6 +58,7 @@ binds: - x-content-type-options ``` + ### Body transformation You can provide a custom body for a request or response. @@ -76,3 +77,7 @@ transformations: '"This is a custom response body."' ``` +## Conditional execution + +To run a transformation only when a CEL expression matches, use the `conditional` field. For example, you can transform internal traffic only and leave external traffic untouched. For details, see [Conditional policies]({{< link-hextra path="/configuration/policies/conditional-policies" >}}). + diff --git a/content/docs/standalone/latest/inference/_index.md b/content/docs/standalone/latest/inference/_index.md index d75b10c45..d8815f80d 100644 --- a/content/docs/standalone/latest/inference/_index.md +++ b/content/docs/standalone/latest/inference/_index.md @@ -6,5 +6,73 @@ description: test: skip --- -Refer to the agentgateway on Kubernetes documentation for more information about inference routing. You can also comment or subscribe to [this issue](https://github.com/agentgateway/agentgateway/issues/620) to get updates on the status of inference support in the standalone agentgateway binary. +Agentgateway supports the Kubernetes Gateway API Inference Extension in two +deployment modes. +## Kubernetes Gateway API mode + +In Kubernetes Gateway API mode, agentgateway runs as the gateway data plane for +Gateway API resources. You install the Inference Extension CRDs, create an +`InferencePool`, and route to that pool from an `HTTPRoute`. The Endpoint Picker +Extension (EPP) acts as an extension service that selects the best model server +endpoint for each inference request. + +Use this mode when you want Gateway API integration, `InferencePool` resources, +traffic splitting, route matching, and other Kubernetes networking features. + +{{< cards>}} + {{< card link="/docs/kubernetes/main/inference/" title="Set up Kubernetes inference routing" >}} +{{< /cards >}} + +## Standalone request scheduler mode + +In standalone request scheduler mode, agentgateway runs as a sidecar proxy with +the EPP. The proxy and EPP communicate over localhost, and agentgateway uses its +standalone `inferenceRouting` local configuration to route requests to a +synthetic service before consulting the EPP for endpoint selection. + +Use this mode for single-tenant or job-scoped workloads where deploying a full +Gateway API stack would add unnecessary operational overhead. In this mode, the +upstream standalone Helm chart can deploy agentgateway as the sidecar proxy with +`proxyType: agentgateway`. + +Standalone request scheduler mode does not support `InferencePool`. The +standalone configuration must define a top-level synthetic service, such as a +`services` entry, and the route backend must reference that service. When EPP +owns endpoint discovery, set `destinationMode: passthrough` so EPP-selected +destinations can be forwarded to directly without matching local workload +endpoint data. + +For example, the standalone agentgateway configuration defines the synthetic +service in `services`, and the route backend references it as +`default/my-model`. + +```yaml +services: +- name: my-model + namespace: default + hostname: my-model + vips: [] + ports: + 8000: 8000 + +binds: +- port: 8081 + listeners: + - routes: + - backends: + - service: + name: default/my-model + port: 8000 + policies: + inferenceRouting: + endpointPicker: + host: 127.0.0.1:9002 + destinationMode: passthrough +``` + +For more examples, see the [standalone EPP example](https://github.com/agentgateway/agentgateway/blob/main/examples/standalone-epp/README.md). + +{{< cards>}} + {{< card link="https://gateway-api-inference-extension.sigs.k8s.io/guides/standalone/#deploy-as-a-standalone-request-scheduler" title="Deploy a standalone request scheduler" icon="external-link" >}} +{{< /cards >}} diff --git a/content/docs/standalone/latest/integrations/_index.md b/content/docs/standalone/latest/integrations/_index.md index ec2d35ab8..e302410f9 100644 --- a/content/docs/standalone/latest/integrations/_index.md +++ b/content/docs/standalone/latest/integrations/_index.md @@ -7,3 +7,5 @@ test: skip --- Integrate agentgateway with your existing infrastructure and tools. + + diff --git a/content/docs/standalone/latest/integrations/networking/cert-manager.md b/content/docs/standalone/latest/integrations/networking/cert-manager.md index 99edb23db..b3440331b 100644 --- a/content/docs/standalone/latest/integrations/networking/cert-manager.md +++ b/content/docs/standalone/latest/integrations/networking/cert-manager.md @@ -4,7 +4,7 @@ weight: 10 description: Automatic TLS certificate management for agentgateway --- -[cert-manager](https://cert-manager.io) automates TLS certificate management in Kubernetes. Use it to automatically provision and renew certificates for agentgateway. +[cert-manager]() automates TLS certificate management in Kubernetes. Use it to automatically provision and renew certificates for agentgateway. ## Why use cert-manager with agentgateway? diff --git a/content/docs/standalone/latest/llm/providers/anthropic.md b/content/docs/standalone/latest/llm/providers/anthropic.md index 9375d9ac3..5dd47ce54 100644 --- a/content/docs/standalone/latest/llm/providers/anthropic.md +++ b/content/docs/standalone/latest/llm/providers/anthropic.md @@ -34,7 +34,7 @@ llm: After running agentgateway with the configuration from the previous section, you can send a request to the `v1/messages` endpoint. Agentgateway automatically adds the `x-api-key` authorization and `anthropic-version` headers to the request. The request is forwarded to the Anthropic API and the response is returned to the client. -```bash +```json curl -X POST http://localhost:4000/v1/messages \ -H "Content-Type: application/json" \ -d '{ diff --git a/content/docs/standalone/latest/llm/providers/azure.md b/content/docs/standalone/latest/llm/providers/azure.md index e21282dcd..7fd8343dd 100644 --- a/content/docs/standalone/latest/llm/providers/azure.md +++ b/content/docs/standalone/latest/llm/providers/azure.md @@ -14,8 +14,8 @@ Before you can use Azure as an LLM provider, you must authenticate by using one Azure supports two endpoint types: -- **Azure AI Foundry** (`Foundry`): Connect to Azure AI Foundry project endpoints at `{resourceName}-resource.services.ai.azure.com`. -- **Azure OpenAI** (`OpenAI`): Connect directly to Azure OpenAI Service deployments at `{resourceName}.openai.azure.com`. +- **Azure AI Foundry** (`foundry`): Connect to Azure AI Foundry project endpoints at `{resourceName}-resource.services.ai.azure.com`. +- **Azure OpenAI** (`openAI`): Connect directly to Azure OpenAI Service deployments at `{resourceName}.openai.azure.com`. {{< reuse "agw-docs/snippets/review-configuration.md" >}} @@ -31,7 +31,7 @@ llm: provider: azure params: azureResourceName: "your-resource-name" - azureResourceType: Foundry + azureResourceType: foundry azureProjectName: "your-project-name" ``` @@ -46,7 +46,7 @@ llm: provider: azure params: azureResourceName: "your-resource-name" - azureResourceType: Foundry + azureResourceType: foundry azureProjectName: "your-project-name" apiKey: "$AZURE_API_KEY" ``` @@ -62,7 +62,7 @@ llm: provider: azure params: azureResourceName: "your-resource-name" - azureResourceType: OpenAI + azureResourceType: openAI ``` {{% /tab %}} @@ -75,8 +75,8 @@ llm: | `name` | The model name to match in incoming requests. When a client sends `"model": ""`, the request is routed to this provider. Use `*` to match any model name. | | `provider` | The LLM provider, set to `azure` for Azure AI models. | | `params.azureResourceName` | The Azure resource name used to construct the endpoint hostname. | -| `params.azureResourceType` | The endpoint type: `Foundry` for Azure AI Foundry, or `OpenAI` for Azure OpenAI Service. | -| `params.azureProjectName` | The Foundry project name. Required for `Foundry` type. If omitted, defaults to `azureResourceName`. | +| `params.azureResourceType` | The endpoint type: `foundry` for Azure AI Foundry, or `openAI` for Azure OpenAI Service. | +| `params.azureProjectName` | The Foundry project name. Required for `foundry` type. If omitted, defaults to `azureResourceName`. | | `params.azureApiVersion` | Optional API version override. Defaults to `v1`. For legacy deployments, use a dated version like `2024-04-01-preview`. | | `params.model` | The specific Azure model to use. If set, this model is used for all requests. If not set, the request must include the model to use. | | `params.apiKey` | The Azure API key for authentication. If unset, implicit Entra ID authentication is used. You can reference environment variables using the `$VAR_NAME` syntax. | @@ -113,7 +113,7 @@ binds: azure: resourceName: "your-resource-name" projectName: "your-project-name" - resourceType: Foundry + resourceType: foundry model: gpt-4.1 ``` @@ -153,7 +153,7 @@ binds: azure: resourceName: "your-resource-name" projectName: "your-project-name" - resourceType: Foundry + resourceType: foundry model: gpt-4.1 ``` @@ -177,7 +177,7 @@ binds: provider: azure: resourceName: "your-resource-name" - resourceType: OpenAI + resourceType: openAI model: gpt-4.1 policies: backendAuth: @@ -217,7 +217,7 @@ binds: provider: azure: resourceName: "your-resource-name" - resourceType: OpenAI + resourceType: openAI model: gpt-4.1 policies: backendAuth: @@ -255,7 +255,7 @@ binds: provider: azure: resourceName: "your-resource-name" - resourceType: OpenAI + resourceType: openAI model: gpt-4.1 policies: backendAuth: @@ -296,7 +296,7 @@ binds: provider: azure: resourceName: "your-resource-name" - resourceType: OpenAI + resourceType: openAI model: gpt-4.1 policies: backendAuth: diff --git a/content/docs/standalone/latest/mcp/connect/openapi.md b/content/docs/standalone/latest/mcp/connect/openapi.md index 392b5481a..d3db0e358 100644 --- a/content/docs/standalone/latest/mcp/connect/openapi.md +++ b/content/docs/standalone/latest/mcp/connect/openapi.md @@ -22,13 +22,11 @@ In your OpenAPI schema, make sure to set the URL of the server. If no URL is set To avoid this issue, explicitly set the URL value to `/` in the OpenAPI schema, such as the following example. ```json -{ - "servers": [ + "servers": [ { "url": "/" } ] -} ``` ### Sample Petstore server {#petstore} diff --git a/content/docs/standalone/latest/operations/agctl.md b/content/docs/standalone/latest/operations/agctl.md new file mode 100644 index 000000000..7d23aa71a --- /dev/null +++ b/content/docs/standalone/latest/operations/agctl.md @@ -0,0 +1,12 @@ +--- +title: Install agctl +weight: 5 +description: Install the agctl command-line tool for inspecting and debugging agentgateway. +test: skip +--- + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} + +{{< reuse "agw-docs/pages/operations/agctl-install.md" >}} diff --git a/content/docs/standalone/latest/operations/debug.md b/content/docs/standalone/latest/operations/debug.md index ff91a83b6..5381e19e6 100644 --- a/content/docs/standalone/latest/operations/debug.md +++ b/content/docs/standalone/latest/operations/debug.md @@ -3,4 +3,140 @@ title: Debug your setup weight: 15 --- -{{< reuse "agw-docs/pages/operations/debug-standalone.md" >}} +Inspect and troubleshoot a standalone agentgateway instance through the admin endpoints and the [`agctl`]({{< link-hextra path="/operations/agctl" >}}) command-line tool. + +## About + +Agentgateway exposes an admin server on `127.0.0.1:15000` by default. The admin server provides the following endpoints for inspection and debugging. + +| Endpoint | Description | +| -- | -- | +| `/config_dump` | Returns the runtime configuration that agentgateway has loaded, including binds, listeners, routes, backends, workloads, services, and policies. | +| `/debug/trace` | Streams a JSON-over-SSE trace of the next request that the proxy handles. The `agctl trace` command consumes this endpoint. | +| `/logging` | Get and set the logging level at runtime. | +| `/memory` | Dump allocator and process memory statistics. | +| `/debug/pprof/profile` | Build a CPU profile by using the [pprof](https://github.com/google/pprof) profiler. Use `?seconds=N` to set the duration (1–300s, default 10s). | +| `/debug/pprof/heap` | Collect heap profiling data. | +| `/debug/tasks` | Inspect the live tokio task tree. | +| `/quitquitquit` | Trigger a graceful shutdown of agentgateway. | + +You can change the admin address by setting the top-level `adminAddr` field in your config file, such as the following. + +```yaml +config: + adminAddr: 127.0.0.1:16000 +``` + +To inspect the proxy's configuration and to capture per-request traces, use the [`agctl`]({{< link-hextra path="/operations/agctl" >}}) command-line tool. `agctl` wraps the admin endpoints and renders their output in formats that are easier to scan than raw JSON. + +## Inspect the loaded configuration + +To dump the configuration that the running proxy has loaded, capture the JSON from the `/config_dump` endpoint and pass it to `agctl config all`. + +1. Save the proxy's config dump to a file. + + ```sh + curl -s http://127.0.0.1:15000/config_dump > /tmp/agw-dump.json + ``` + +2. Render it with `agctl`. Use `-o yaml` for a more readable view. + + ```sh + agctl config all --file /tmp/agw-dump.json -o yaml + ``` + +For complete steps, see [Inspect agentgateway configuration]({{< link-hextra path="/operations/inspect-config" >}}). + +## Trace requests + +To capture a per-request trace as agentgateway processes it, use `agctl trace`. The trace shows you the route that was selected, the policies that were applied, the backend that was chosen, and the response status. Tracing is invaluable for understanding why a request matched (or did not match) a route, why a policy was or was not applied, or why a request returned an unexpected status. + +1. In one terminal, start a watch. + + ```sh + agctl trace --local + ``` + +2. In another terminal, send a request. + + ```sh + curl http://127.0.0.1:3000/headers + ``` + + `agctl` opens a text-based terminal user interface (TUI) that walks you through the request and response lifecycle. Use `--raw` to print JSON Lines instead. + +For complete steps, including how to inject a request from `agctl` itself, see [Trace requests with agctl]({{< link-hextra path="/operations/trace-requests" >}}). + +## Enable debug logs {#debug-logs} + +Agentgateway uses the same level syntax as [`RUST_LOG`](https://docs.rs/env_logger/latest/env_logger/#enabling-logging): `error`, `warn`, `info`, `debug`, and `trace`. You can change the level at runtime through the `/logging` endpoint, or set it in your config file at startup. + +{{< tabs tabTotal="2" items="curl logging endpoint,config file" >}} +{{% tab tabName="curl logging endpoint" %}} +Set the log level without restarting agentgateway. If you configured agentgateway to use a different admin address, update the host and port accordingly. + +```sh +curl -X POST "http://localhost:15000/logging?level=debug" +``` + +Example output: + +``` +current log level is typespec_client_core::http::policies::logging=warn,hickory_server::server::server_future=off,rmcp=warn,debug +``` +{{% /tab %}} +{{% tab tabName="config file" %}} +Set the log level permanently. Agentgateway reads the value at startup. + +```yaml +config: + logging: + level: debug + # optional: default is text + format: json +``` +{{% /tab %}} +{{< /tabs >}} + +The agentgateway process now writes `debug` log lines, such as the following. + +``` +2026-02-12T16:11:25.493503Z debug proxy::httpproxy request before normalization: Request { method: OPTIONS, uri: /sse?sessionId=... +``` + +You can also set fine-grained levels per module by using the same `RUST_LOG` filter syntax, such as `info,proxy::httpproxy=trace`. + +## Capture profiles + +Agentgateway includes pprof endpoints to help you investigate CPU and memory issues. + +1. Optional: If you have not already, download [Graphviz](https://graphviz.org/download/) to visualize the profiles. + +2. Capture a CPU profile. The default duration is 10 seconds; the example uses 30 seconds. + + ```sh + curl -o cpu.pprof "http://127.0.0.1:15000/debug/pprof/profile?seconds=30" + ``` + +3. Capture a heap profile. + + ```sh + curl -o heap.pprof http://127.0.0.1:15000/debug/pprof/heap + ``` + +4. Inspect the profile with `go tool pprof`. + + **CPU profile** + ```sh + go tool pprof -http=: cpu.pprof + ``` + + **Heap profile** + ```sh + go tool pprof -http=: heap.pprof + ``` + + Graphviz opens on your web browser to a UI on localhost. Example: + + {{< reuse-image src="img/debug-heap-pprof.png" caption="Heap profile graph" >}} + {{< reuse-image-dark srcDark="img/debug-heap-pprof.png" caption="Heap profile graph" >}} diff --git a/content/docs/standalone/latest/operations/inspect-config.md b/content/docs/standalone/latest/operations/inspect-config.md new file mode 100644 index 000000000..7335b51e6 --- /dev/null +++ b/content/docs/standalone/latest/operations/inspect-config.md @@ -0,0 +1,12 @@ +--- +title: Inspect agentgateway configuration +weight: 17 +description: Inspect the runtime configuration that a standalone agentgateway instance has loaded. +test: skip +--- + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} + +{{< reuse "agw-docs/pages/operations/inspect-config-standalone.md" >}} diff --git a/content/docs/standalone/latest/operations/trace-requests.md b/content/docs/standalone/latest/operations/trace-requests.md new file mode 100644 index 000000000..8bcea8114 --- /dev/null +++ b/content/docs/standalone/latest/operations/trace-requests.md @@ -0,0 +1,15 @@ +--- +title: Trace requests with agctl +weight: 16 +description: Capture a per-request trace as a standalone agentgateway instance handles the request. +test: + trace-validate: + - file: content/docs/standalone/latest/operations/trace-requests.md + path: trace-validate +--- + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} + +{{< reuse "agw-docs/pages/operations/trace-requests-standalone.md" >}} diff --git a/content/docs/standalone/latest/reference/agctl/_index.md b/content/docs/standalone/latest/reference/agctl/_index.md new file mode 100644 index 000000000..e2d3454f2 --- /dev/null +++ b/content/docs/standalone/latest/reference/agctl/_index.md @@ -0,0 +1,14 @@ +--- +title: agctl CLI reference +weight: 25 +description: Reference for the agctl command-line tool. +test: skip +--- + +Reference for `agctl`, the command-line tool that you use to inspect and debug agentgateway. The pages in this section are generated from the `agctl` source. + +To install `agctl`, see [Install agctl]({{< link-hextra path="/operations/agctl" >}}). + +{{< callout type="warning">}} +{{< reuse "agw-docs/snippets/feature-experimental.md">}} +{{< /callout >}} diff --git a/content/docs/standalone/latest/reference/agctl/agctl-config-all.md b/content/docs/standalone/latest/reference/agctl/agctl-config-all.md new file mode 100644 index 000000000..fb84cb294 --- /dev/null +++ b/content/docs/standalone/latest/reference/agctl/agctl-config-all.md @@ -0,0 +1,8 @@ +--- +title: agctl config all +weight: 76 +description: Reference for the `agctl config all` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-config-all.md" >}} diff --git a/content/docs/standalone/latest/reference/agctl/agctl-config-backends.md b/content/docs/standalone/latest/reference/agctl/agctl-config-backends.md new file mode 100644 index 000000000..a5be64573 --- /dev/null +++ b/content/docs/standalone/latest/reference/agctl/agctl-config-backends.md @@ -0,0 +1,8 @@ +--- +title: agctl config backends +weight: 90 +description: Reference for the `agctl config backends` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-config-backends.md" >}} diff --git a/content/docs/standalone/latest/reference/agctl/agctl-config.md b/content/docs/standalone/latest/reference/agctl/agctl-config.md new file mode 100644 index 000000000..f06d01bf0 --- /dev/null +++ b/content/docs/standalone/latest/reference/agctl/agctl-config.md @@ -0,0 +1,8 @@ +--- +title: agctl config +weight: 118 +description: Reference for the `agctl config` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-config.md" >}} diff --git a/content/docs/standalone/latest/reference/agctl/agctl-trace.md b/content/docs/standalone/latest/reference/agctl/agctl-trace.md new file mode 100644 index 000000000..2fbe069e4 --- /dev/null +++ b/content/docs/standalone/latest/reference/agctl/agctl-trace.md @@ -0,0 +1,8 @@ +--- +title: agctl trace +weight: 115 +description: Reference for the `agctl trace` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl-trace.md" >}} diff --git a/content/docs/standalone/latest/reference/agctl/agctl.md b/content/docs/standalone/latest/reference/agctl/agctl.md new file mode 100644 index 000000000..961cdc260 --- /dev/null +++ b/content/docs/standalone/latest/reference/agctl/agctl.md @@ -0,0 +1,8 @@ +--- +title: agctl +weight: 10 +description: Reference for the `agctl` command. +test: skip +--- + +{{< reuse "agw-docs/pages/reference/agctl/main/agctl.md" >}} diff --git a/content/docs/standalone/latest/reference/release-notes.md b/content/docs/standalone/latest/reference/release-notes.md index fd557b3ec..80c3a7809 100644 --- a/content/docs/standalone/latest/reference/release-notes.md +++ b/content/docs/standalone/latest/reference/release-notes.md @@ -1,93 +1,94 @@ ---- -title: Release notes -weight: 20 ---- - -Review the release notes for agentgateway standalone. - -{{< callout type="info">}} -For more details, review the [GitHub release notes in the agentgateway repository](https://github.com/agentgateway/agentgateway/releases) -{{< /callout >}} - -## πŸ”₯ Breaking changes {#v11-breaking-changes} - -### MCP authentication moved to route level - - - -MCP authentication is configured at the route level under `policies.mcpAuthentication`. The route-level placement aligns MCP auth with other route-level policies and allows JWT claims to be used in authorization, rate limiting, and transformation policies. - -* **Before**: MCP authentication was configured as a backend-level policy. -* **After**: MCP authentication is configured under `routes[].policies.mcpAuthentication`. - -No YAML structure changes are required for standalone users, as standalone configuration already placed `mcpAuthentication` under route policies. However, if you have automation or tooling that references MCP authentication as a backend-level concept, update it accordingly. - -For more information, see [MCP authentication]({{< link-hextra path="/configuration/security/mcp-authn/" >}}). - -## 🌟 New features {#v11-new-features} - -### OIDC browser authentication - - - -A new `oidc` route policy provides built-in OpenID Connect browser authentication with PKCE support, encrypted session cookies, and automatic redirect handling. The OIDC policy is a native alternative to deploying an external proxy like oauth2-proxy. - -```yaml -policies: - oidc: - issuer: http://keycloak.example.com/realms/myrealm - clientId: agentgateway-browser - clientSecret: my-secret - redirectURI: http://localhost:3000/oauth/callback - scopes: - - profile - - email -``` - -For more information, see [OIDC browser authentication]({{< link-hextra path="/configuration/security/oidc/" >}}). - -### L4 network authorization - - - -A new `networkAuthorization` frontend policy enables Layer 4 network authorization for non-HTTP traffic. You can enforce policies based on source IP, port, and mTLS client identity before HTTP processing begins. Combine with HTTP authorization for layered L4+L7 controls. - -```yaml -frontendPolicies: - networkAuthorization: - rules: - - allow: 'source.address.startsWith("10.")' - - require: 'source.tls.identity == "spiffe://cluster.local/ns/default/sa/my-service"' -``` - -For more information, see [Network authorization]({{< link-hextra path="/configuration/security/network-authz/" >}}). - -### Authorization require rules - -Authorization policies now support `require` rules in addition to `allow` and `deny`. The `require` rule type provides clearer semantics for expressing mandatory conditions. All `require` rules must match for the request to proceed. - -```yaml -authorization: - rules: - - require: 'jwt.aud == "my-service"' -``` - -For more information, see [HTTP authorization]({{< link-hextra path="/configuration/security/http-authz/" >}}). - -### MCP improvements - -- **Stateless sessions**: MCP upstreams can now use stateless sessions, avoiding state persistence for backends that don't need it. For more information, see [OpenAPI connectivity]({{< link-hextra path="/mcp/connect/openapi/" >}}) and [Backends]({{< link-hextra path="/configuration/backends/" >}}). -- **Explicit service reference lists**: MCP backends can specify targets with explicit service references. -- **Tool payloads in CEL context**: Tool names and payloads are available in logging CEL expressions via `mcp.tool.name` and other `mcp.tool.*` fields. - -### LLM gateway enhancements - -- **Path prefixes**: LLM providers now support `pathPrefix` for custom API base paths. For more information, see [Providers]({{< link-hextra path="/llm/providers/" >}}). -- **Azure default authentication**: Azure OpenAI providers can use platform-default authentication. For more information, see [Azure]({{< link-hextra path="/llm/providers/azure/" >}}). -- **Vertex region optional**: Vertex AI region configuration is now optional with a global default. For more information, see [Vertex AI]({{< link-hextra path="/llm/providers/vertex/" >}}). - -### Gateway and routing improvements - -- **Automatic protocol detection**: A new `auto` bind protocol peeks at the first connection byte to determine TLS vs HTTP, simplifying mixed-protocol environments. For more information, see [Listeners]({{< link-hextra path="/configuration/listeners/" >}}). -- **Service SANs for upstream TLS**: Upstream TLS now respects Subject Alternative Names from Kubernetes Services. For more information, see [Backend TLS]({{< link-hextra path="/configuration/security/backend-tls/" >}}). -- **CEL hash functions**: New `sha1.encode`, `sha256.encode`, and `md5.encode` functions are available in CEL expressions. For more information, see [CEL expressions]({{< link-hextra path="/reference/cel/" >}}). +--- +title: Release notes +weight: 20 +description: Review the release notes for agentgateway standalone. +test: skip +--- + +Review the release notes for agentgateway standalone. + +{{< callout type="info">}} +For more details, review the [GitHub release notes in the agentgateway repository](https://github.com/agentgateway/agentgateway/releases) +{{< /callout >}} + +## 🌟 New features {#v12-new-features} + +### Conditional policy execution + +Policies can now be selected conditionally using CEL expressions. Conditional execution is supported for external auth, transformations, rate limiting, external processing, and direct responses. The first matching policy is applied, with an optional final fallback entry. For more information, see [Conditional policies]({{< link-hextra path="/configuration/policies/conditional-policies/" >}}). + +### Route delegation + +Agentgateway now supports route delegation, allowing parent routes to delegate portions of their routing tree to child routes. Platform teams can own shared parent routes while application teams manage delegated route fragments. For more information, see [Route delegation]({{< link-hextra path="/configuration/traffic-management/route-delegation/" >}}). + +### `agctl` CLI debugging tool + +A new experimental `agctl` command-line tool for inspecting and debugging agentgateway is now available. To install `agctl`, see [Install agctl]({{< link-hextra path="/operations/agctl/" >}}). +- `agctl config` renders the runtime configuration that an agentgateway proxy has loaded, including binds, listeners, routes, backends, workloads, and policies, as a structured table, JSON, or YAML. `agctl config backends` shows per-backend health, request counts, and latency. For more information, see [Inspect agentgateway configuration]({{< link-hextra path="/operations/inspect-config/" >}}). +- `agctl trace` streams a step-by-step trace of how the proxy processes the next request, showing the matched route, applied policies, chosen backend, and response status. For more information, see [Trace requests with agctl]({{< link-hextra path="/operations/trace-requests/" >}}). + +For a complete command reference, see [agctl CLI reference]({{< link-hextra path="/reference/agctl/" >}}). + +### Policy targets + +Policies can now target resources by label selector in addition to explicit attachment points. This configuration makes it easier to apply shared policy configuration across groups of listeners, routes, or backends. For more information, see [Attachment points]({{< link-hextra path="/configuration/policies/attachment/" >}}). + +### PROXY protocol support + +Listeners now support downstream PROXY protocol handling, including strict and optional modes and PROXY protocol v1/v2 selection. For more information, see [Listeners]({{< link-hextra path="/configuration/listeners/" >}}). + +### Locality load balancing and failover + +The data plane now supports locality-aware load balancing and failover, improving traffic placement for multi-zone and multi-region deployments. + +### LLM gateway enhancements + +- **Azure provider**: A new Azure provider supports both Azure OpenAI and Azure AI Foundry style resources. For more information, see [Azure OpenAI]({{< link-hextra path="/integrations/llm-providers/azure-openai/" >}}). +- **Copilot support**: Added Copilot authentication and LLM provider support. +- **Gemini Responses API**: Responses API requests can now be routed to Gemini. For more information, see [Google Gemini]({{< link-hextra path="/integrations/llm-providers/gemini/" >}}). +- **Path prefixes**: Custom path prefixes now work across all LLM providers, including Gemini, Vertex, Bedrock, and Azure. +- **OpenAI compatibility**: OpenAI chat completion requests now normalize `max_tokens` to `max_completion_tokens`. +- **Azure Content Safety guardrails**: Prompt and response guardrails can now use Azure AI Content Safety. +- **Bedrock guardrails masking**: Bedrock guardrails now support masking. For more information, see [Amazon Bedrock]({{< link-hextra path="/integrations/llm-providers/bedrock/" >}}). + +### MCP improvements + +- **Session TTL**: MCP sessions can now be configured with an idle TTL. For more information, see [MCP connectivity]({{< link-hextra path="/mcp/" >}}). +- **Stateless MCP**: Stateless MCP initialization and shutdown behavior is improved. +- **List resources with multiplexing**: `ListResourcesRequest` now works with multiplexed MCP targets. + +### Authentication and authorization + +- **Backend external auth**: External auth can now run as a backend policy after backend selection. For more information, see [External authorization]({{< link-hextra path="/configuration/security/external-authz/" >}}). +- **Auth credential locations**: JWT, basic auth, API key, and backend auth can now override where credentials are read from or inserted, including headers, query parameters, and cookies. For more information, see [JWT authentication]({{< link-hextra path="/configuration/security/jwt-authn/" >}}) and [API key authentication]({{< link-hextra path="/configuration/security/apikey-authn/" >}}). +- **Explicit GCP credentials**: GCP backend auth can now use explicit Secret-backed credentials. For more information, see [Backend authentication]({{< link-hextra path="/configuration/security/backend-authn/" >}}). + +### Traffic, TLS, and networking + +- **Post-quantum TLS**: TLS configuration now supports post-quantum key exchange groups, including `X25519_MLKEM768`. For more information, see [Listeners]({{< link-hextra path="/configuration/listeners/" >}}). +- **Max connection duration**: HTTP listeners can now enforce a maximum connection duration. +- **HTTP/2 pooling**: HTTP/2 connection pooling is improved to avoid the single-connection bottleneck. + +### Operations + +- Agentgateway's memory allocator performance is improved, resulting in increased runtime performance and decreased memory utilization. +- A new `/debug/pprof/heap` endpoint is available to get a `pprof` snapshot of current and historical allocations. + +### Telemetry + +- **Custom Prometheus labels**: Policies can add custom Prometheus metric labels using CEL expressions. For more information, see [Metrics]({{< link-hextra path="/reference/observability/metrics/" >}}). +- **OpenTelemetry environment variables**: The OTEL configuration now respects standard environment variables. For more information, see [Traces]({{< link-hextra path="/reference/observability/traces/" >}}). + +## πŸͺ² Notable fixes {#v12-fixes} + +- Fixed A2A policy matching for agents hosted under sub-paths. +- Fixed A2A and MCP handling of `X-Forwarded-Proto`. +- Fixed policies that target missing or non-existent backends from silently attaching to the gateway. +- Fixed JWKS stale fetches, startup fetch behavior, cache cleanup, and orphan cleanup. +- Fixed CEL property parsing after bracket accessors. +- Fixed CEL `response.body` access when upstream responses are compressed. +- Fixed request body buffering when CEL expressions do not need the body. +- Fixed `Host` and `:authority` alignment after header mutation. +- Fixed stripping of hop-by-hop connection headers and encoding headers for more consistent behavior. +- Fixed invalid htpasswd entries to fail gracefully instead of breaking basic auth handling. +- Fixed active stream accounting in the connection pool when debug assertions are disabled. diff --git a/content/docs/standalone/latest/tutorials/tailscale-auth/_index.md b/content/docs/standalone/latest/tutorials/tailscale-auth/_index.md index b70c40167..f86f23ee4 100644 --- a/content/docs/standalone/latest/tutorials/tailscale-auth/_index.md +++ b/content/docs/standalone/latest/tutorials/tailscale-auth/_index.md @@ -224,7 +224,7 @@ info request ... tailscale.node=your-machine-name tailscale.email=you@example.co ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Client │────▢│ agentgateway │────▢│ Tailscale Daemonβ”‚ +β”‚ Client │────▢│agentgateway │────▢│ Tailscale Daemonβ”‚ β”‚(100.x.x.x) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ diff --git a/content/docs/standalone/main/_index.md b/content/docs/standalone/main/_index.md index fb11caa00..af63f7976 100644 --- a/content/docs/standalone/main/_index.md +++ b/content/docs/standalone/main/_index.md @@ -1,7 +1,7 @@ --- linkTitle: "Main (in dev)" -title: Main (dev) -description: Welcome to the main (dev) standalone docs for agentgateway. +title: Main (in dev) +description: Use agentgateway as a standalone binary. test: skip --- diff --git a/hugo.yaml b/hugo.yaml index a9ba0adee..611a7c622 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -152,22 +152,22 @@ params: sections: standalone: versions: - - version: "1.2.x" + - version: "1.3.x" dropdown: "main" linkVersion: "main" url: "/main" - - version: "1.1.x" - dropdown: "1.1 (latest)" + - version: "1.2.x" + dropdown: "1.2 (latest)" linkVersion: "latest" url: "/latest" kubernetes: versions: - - version: "1.2.x" + - version: "1.3.x" dropdown: "main" linkVersion: "main" url: "/main" - - version: "1.1.x" - dropdown: "1.1 (latest)" + - version: "1.2.x" + dropdown: "1.2 (latest)" linkVersion: "latest" url: "/latest"