Skip to content

Added more detailed transformations topics AND added local link checking on PRs#328

Open
kristin-kronstain-brown wants to merge 37 commits intomainfrom
kkb-transformations
Open

Added more detailed transformations topics AND added local link checking on PRs#328
kristin-kronstain-brown wants to merge 37 commits intomainfrom
kkb-transformations

Conversation

@kristin-kronstain-brown
Copy link
Collaborator

@kristin-kronstain-brown kristin-kronstain-brown commented Mar 20, 2026

Added new transformation topics, updated from kgateway. Link checker changes also included to be able to check a subset of files effectively.

Link checker changes

Adds pull request support to the link checker workflow. Previously the workflow only ran on a schedule (Monday) and manual dispatch, checking all links across all files and reporting results via GitHub issues and Slack. PRs now trigger a faster, targeted check.

Trigger changes

  • Workflow now runs on pull requests that modify content/** or assets/**
  • Cron and dispatch behavior is unchanged

On pull requests

  • Checks only the HTML pages built from changed .md files; falls back to all HTML if no content files changed (e.g. asset-only PRs)
  • Skips external link checking (--exclude "^https://") — only local/internal links are validated; remapped links are still resolved to local files and checked
  • Skips issue creation and Slack notification
  • Fails the check and writes the markdown report to the job summary if errors are found
  • Artifacts are still uploaded for review

On cron/dispatch

  • No behavior change: checks all links across all files, creates a GitHub issue and posts to Slack on failure

Other

  • fetch-depth: 100 on checkout so git diff can find the merge base for the changed-files calculation
  • Lychee invocation refactored to use a bash array, which correctly handles arguments containing special characters

@kristin-kronstain-brown kristin-kronstain-brown changed the title Added transformations topics Added more detailed transformations topics Mar 20, 2026
Signed-off-by: Kristin Brown <[email protected]>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 23, 2026

Deploying agentproxy with  Cloudflare Pages  Cloudflare Pages

Latest commit: 893766f
Status: ✅  Deploy successful!
Preview URL: https://fdc0e034.agentproxy.pages.dev
Branch Preview URL: https://kkb-transformations.agentproxy.pages.dev

View logs

Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
Signed-off-by: Kristin Brown <[email protected]>
@kristin-kronstain-brown kristin-kronstain-brown changed the title Added more detailed transformations topics Added more detailed transformations topics AND added local link checking on PRs Mar 25, 2026



## Context variables {#context-variables}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a sentence about what context variables are

{{% github-table url="https://raw.githubusercontent.com/agentgateway/agentgateway/refs/heads/main/schema/cel.md" section="CEL context Schema" %}}


## Built-in functions {#cel-functions}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a sentence what built-in functions are


### Function examples

These functions are used in the documentation examples in this section.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a table with topic name and then used function?

@@ -1,21 +1,20 @@
Use an Inja template to extract a value from a request header and add it as a header to your responses.
Use [CEL expressions]({{< link-hextra path="/reference/cel/" >}}) to inject, modify, and remove headers in requests and responses. The example uses `request.headers[]` to extract a header value and combines `set`, `add`, and `remove` operations in a single transformation.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Use [CEL expressions]({{< link-hextra path="/reference/cel/" >}}) to inject, modify, and remove headers in requests and responses. The example uses `request.headers[]` to extract a header value and combines `set`, `add`, and `remove` operations in a single transformation.
Use [CEL expressions]({{< link-hextra path="/reference/cel/" >}}) to inject, modify, and remove response headers. The example uses the `request.headers[]` context variable to extract a request header value and injects the value into a response header. You also explore how to combine `set`, `add`, and `remove` operations in a single transformation.

{{% tab tabName="Envoy-based kgateway" %}}
```yaml

The gateway intercepts the upstream response and modifies its headers before returning it to the client. You can combine `set`, `add`, and `remove` in a single policy so that the gateway applies all three operations in one pass. This configuration is useful when you need to enrich responses with values from the original request, enforce header policies like CORS, or strip internal headers that should not reach the client.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The gateway intercepts the upstream response and modifies its headers before returning it to the client. You can combine `set`, `add`, and `remove` in a single policy so that the gateway applies all three operations in one pass. This configuration is useful when you need to enrich responses with values from the original request, enforce header policies like CORS, or strip internal headers that should not reach the client.
The gateway intercepts the upstream response and modifies its headers before returning them to the client. You can combine `set`, `add`, and `remove` operations in a single policy so that the gateway applies all three operations in one pass. This configuration is useful when you need to enrich responses with values from the original request or strip internal headers that should not reach the client.

In this example, all three operations are applied together:

* `set`: Extracts the `x-gateway-request` request header value and sets it as the `x-gateway-response` response header. Also injects a static `x-response-raw` header with the value `hello`. Use `set` to create a header or overwrite it if it already exists.
* `add`: Appends `https://example.com` to the `access-control-allow-origin` header. Because httpbin already returns `access-control-allow-origin: *`, the response ends up with two entries for that header. Use `add` when you want to append a value without overwriting what is already present.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `add`: Appends `https://example.com` to the `access-control-allow-origin` header. Because httpbin already returns `access-control-allow-origin: *`, the response ends up with two entries for that header. Use `add` when you want to append a value without overwriting what is already present.
* `add`: Appends `https://example.com` to the `access-control-allow-origin` header. Because httpbin already returns `access-control-allow-origin: *`, the response ends up with two entries for that header. Use `add` when you want to append or add a value without overwriting what is already present.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make this a list for each header and explain what happens for each? similar to what we did previously (removed lines).

{{< /tabs >}}
{{< /doc-test >}}

2. Send a request to the httpbin app and include the `x-gateway-request` request header. Verify that you get back a 200 HTTP response code and that the response includes the injected headers, contains two `access-control-allow-origin` values, and omits `access-control-allow-credentials`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe list the expected outcome for each header in a bulleted list

Copy link
Collaborator

@Nadine2016 Nadine2016 Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

appending is weird, typically appending means I still have one header but with multiple values. The fact that you get back the same header with different values, smells like a bug to me. I don't think a system would be able to process that correctly

asked in slack, seems like this is the expectation, but in this case it is not appended, it is just added https://solo-io-corp.slack.com/archives/C08P050QFGF/p1774463768717419

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in order to show the removal, you would technically need to show an example without the policy to prove the header is actually there

In this example, two headers are injected on every incoming request before it is forwarded upstream:

* `x-request-id`: A unique UUIDv4 generated by `uuid()`, used to correlate the request across services.
* `x-sampling-decision`: A random float from `random()`. A tracing backend configured to sample when the value is less than `0.1` records roughly 10% of all requests and ignores the rest.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `x-sampling-decision`: A random float from `random()`. A tracing backend configured to sample when the value is less than `0.1` records roughly 10% of all requests and ignores the rest.
* `x-sampling-decision`: A random float from the `random()` function. For example, you might have a tracing backend that is configured to sample requests only if the `x-sampling-decision` header is set to `0.1` or less.

@@ -0,0 +1,189 @@
Use [CEL expressions]({{< link-hextra path="/reference/cel/" >}}) to encode and decode base64 values in request headers and add the results as response headers. The examples use `base64.encode()`, `base64.decode()`, `bytes()`, `string()`, and `request.headers[]`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Use [CEL expressions]({{< link-hextra path="/reference/cel/" >}}) to encode and decode base64 values in request headers and add the results as response headers. The examples use `base64.encode()`, `base64.decode()`, `bytes()`, `string()`, and `request.headers[]`.
Use [CEL expressions]({{< link-hextra path="/reference/cel/" >}}) to encode and decode base64 values in request headers and add the results as response headers. The examples use the `base64.encode()`, `base64.decode()`, `bytes()`, and `string()` CEL functions, and the `request.headers[]` context variables.

response:
set:
- name: x-user-id-encoded
value: 'base64.encode(bytes(request.headers["x-user-id"]))'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does bytes do here? It looks like the transformation works without it

@@ -0,0 +1,17 @@
---
title: Forward request URLs
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: Forward request URLs
title: Create redirect URLs

---
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: Use CEL expressions to construct a full request URL from context variables and forward it upstream as a request header.
description: Use CEL expressions to construct a redirect URL from context variables and forward it upstream as a request header.

"curl/8.7.1"
],
"X-Forwarded-Uri": [
"http://www.example.com:80/get"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"http://www.example.com:80/get"
"http://www.example.com/get"

@@ -0,0 +1,167 @@
Change the request path and HTTP method when a request header is present by using [CEL expressions]({{< link-hextra path="/reference/cel/" >}}). The example uses `request.headers[]`, `request.path`, and `request.method` with a ternary expression to conditionally set the `:path` and `:method` pseudo headers.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this example does not use query parameters. We should rename that to something like: Change request path and method


## Remove request headers

In this example, you remove the `x-internal-token` request header before the request is forwarded to the upstream. This configuration prevents internal credentials from being exposed to the backend service.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe show a request with that header so that people can see it is there.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This configuration prevents internal credentials from being exposed to the backend service.

this is very specific to the name of the header that was chosen. I think in general you would use this to get rid of headers that you do not want to send to backend services


## Inject request header fields into a response body

In this example, you set the response body to a JSON string built from request context variables. The gateway intercepts the upstream response and replaces the body with the CEL expression result before returning it to the client. The upstream never sees the change — only the client receives the modified body. This is useful for tying responses back to request traces.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In this example, you set the response body to a JSON string built from request context variables. The gateway intercepts the upstream response and replaces the body with the CEL expression result before returning it to the client. The upstream never sees the change — only the client receives the modified body. This is useful for tying responses back to request traces.
In this example, you set the response body to a JSON string built from request context variables. The gateway intercepts the upstream response and replaces the body with the CEL expression result before returning it to the client. The upstream never sees the change — only the client receives the modified body.

```sh
curl -vi http://$INGRESS_GW_ADDRESS:80/get \
-H "host: www.example.com:80" \
-H "x-request-id: alice"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use an ID instead of the name

{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}}


## Inject request header fields into a response body
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Inject request header fields into a response body
## Inject request header fields into a request body


## Inject request body fields into a response body

In this example, you parse a JSON request body using `json()` to extract a field and include it in the response body. Use `request.body` to access the raw incoming request body as a string.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In this example, you parse a JSON request body using `json()` to extract a field and include it in the response body. Use `request.body` to access the raw incoming request body as a string.
In this example, you parse a JSON request body by using the `json()` function to extract a field and include it in the response body. Use `request.body` to access the raw incoming request body as a string.

@@ -0,0 +1,26 @@
---
title: Inject response bodies
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: Inject response bodies
title: Inject response body fields

@@ -0,0 +1,17 @@
---
title: Change response bodies
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: Change response bodies
title: Update response status


## Change the response status on a route

In this example, the transformation applies after routing and targets a specific HTTPRoute. You change the value of the `:status` response header to 401 if the request URI contains `foo=bar`. If the request URI does not contain `foo=bar`, you return a 403 HTTP response code.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In this example, the transformation applies after routing and targets a specific HTTPRoute. You change the value of the `:status` response header to 401 if the request URI contains `foo=bar`. If the request URI does not contain `foo=bar`, you return a 403 HTTP response code.
In this example, the transformation applies after routing and targets a specific HTTPRoute. You change the value of the `:status` response header to 401 if the request URI contains the `foo=bar` query parameter. If the request URI does not contain `foo=bar`, you return a 403 HTTP response code.

@@ -0,0 +1,105 @@
Use the `filterKeys()` and `merge()` [CEL functions]({{< link-hextra path="/reference/cel/#functions-policy-all" >}}) together with `json()` and `toJson()` to sanitize a JSON request body before it reaches the upstream. `filterKeys()` removes unwanted fields by testing each key against a predicate. `merge()` combines two maps, with the second map's values overwriting any matching keys in the first. `toJson()` serializes the resulting map back to a JSON string.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Use the `filterKeys()` and `merge()` [CEL functions]({{< link-hextra path="/reference/cel/#functions-policy-all" >}}) together with `json()` and `toJson()` to sanitize a JSON request body before it reaches the upstream. `filterKeys()` removes unwanted fields by testing each key against a predicate. `merge()` combines two maps, with the second map's values overwriting any matching keys in the first. `toJson()` serializes the resulting map back to a JSON string.
Use the `filterKeys()` and `merge()` [CEL functions]({{< link-hextra path="/reference/cel/#functions-policy-all" >}}) together with `json()` and `toJson()` to sanitize a JSON request body before it reaches the upstream. `filterKeys()` removes unwanted fields by testing each key against a predicate. `merge()` combines two maps, with the second map's value overwriting any matching keys in the first. `toJson()` serializes the resulting map back to a JSON string.

{{< /doc-test >}}

The expression breaks down as follows:
* `json(request.body)`: Parses the raw request body string into a map.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

into a map or JSON?


## Log specific variables

Map individual attribute names to CEL expressions. The attribute names on the left (`request_path`, `request_method`, `client_ip`) are arbitrary and become the keys in the structured log output.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what we mean with on the left?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe just say: Add access log attributes with CEL expressions.


{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}}

## Log specific variables
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Log specific variables
## Log specific request data


Add a `filter` CEL expression to log only requests that match a condition. This configuration is useful for reducing log volume by capturing only error responses or specific traffic patterns.

1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource with a `filter` field set to a CEL expression. Logs are written only when the expression evaluates to `true`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource with a `filter` field set to a CEL expression. Logs are written only when the expression evaluates to `true`.
1. Create an {{< reuse "agw-docs/snippets/trafficpolicy.md" >}} resource with a `filter` field that logs requests only if the response status code does not equal 400.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants