Skip to content

feat(guardrails): support outbound header modification#535

Open
SantiagoDePolonia wants to merge 1 commit into
mainfrom
fix/headers-altering
Open

feat(guardrails): support outbound header modification#535
SantiagoDePolonia wants to merge 1 commit into
mainfrom
fix/headers-altering

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add conditional outbound header set, copy, and remove rules for translated, passthrough, and realtime requests
  • expose intended header deltas through the existing audit request-revision chain and dashboard, respecting LOGGING_LOG_HEADERS and credential redaction
  • keep credentials, transport headers, and payload metadata protected, and prevent request-scoped mutations from leaking into auxiliary LLM calls
  • support explicit empty-value predicates and document configuration and audit semantics

Testing

  • go test ./...
  • make test-dashboard
  • make lint
  • pre-commit hooks: race tests, performance guard, go fix check, and Mint validation

Summary by CodeRabbit

  • New Features

    • Added conditional header modification guardrails for outbound provider requests.
    • Supports setting, removing, and copying header values based on inbound header conditions.
    • Applies to translated, passthrough, and realtime requests while protecting credential and transport headers.
    • Added dashboard controls for creating and editing header conditions and actions.
  • Audit & Documentation

    • Header changes are now recorded in request revisions and displayed in the dashboard’s “Rewritten” view.
    • Expanded guardrail documentation and configuration examples.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds header_modification guardrails that match inbound headers and apply ordered outbound set/remove actions. Mutations flow through request context into translated, passthrough, realtime, and provider requests, while audit revisions and dashboard views expose header deltas.

Changes

Header modification guardrails

Layer / File(s) Summary
Configuration and audit contracts
config/guardrails.go, config/config.example.yaml, internal/auditlog/auditlog.go, docs/advanced/guardrails.mdx, cmd/gomodel/docs/docs.go, docs/openapi.json, CLAUDE.md
Defines header conditions/actions and documents audit revision header deltas and runtime restrictions.
Mutation model and context propagation
internal/core/header_mutation.go, internal/core/header_mutation_test.go
Adds protected-header-aware mutation merging, application, and request-context storage.
Guardrail validation and evaluation
internal/guardrails/header_modification.go, internal/guardrails/definitions.go, internal/guardrails/header_modification_test.go
Validates and normalizes configurations, evaluates predicates, builds ordered mutations, and exposes the new guardrail type to pipelines and the UI.
Request resolution and outbound application
internal/app/app.go, internal/workflows/service.go, internal/server/*, internal/llmclient/*
Resolves mutators after workflow selection, records applied header revisions, and applies mutations at outbound request boundaries.
Dashboard editing and revision display
internal/admin/dashboard/static/js/modules/guardrails.js, internal/admin/dashboard/templates/page-guardrails.html, internal/admin/dashboard/static/js/modules/audit-list.js, internal/admin/dashboard/static/css/dashboard.css
Adds editable condition/action rows, payload sanitization, and rendering for header-only and body rewrite revisions.
Internal execution isolation
internal/responsecache/*, internal/server/internal_chat_completion_executor*
Prevents provider-request header mutations from propagating to internal embedding and chat execution contexts.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Poem

A rabbit hops through headers bright,
Sets and removes bits just right.
Guards the keys, records the trail,
Shows rewritten tales without fail.
“Hop-hop!” says Bun, “the rules now flow!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main change: adding outbound header modification support to guardrails.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/headers-altering

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mintlify

mintlify Bot commented Jul 14, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview Jul 14, 2026, 11:23 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown

Confidence Score: 4/5

Mostly safe, with one contained realtime signaling bug to fix before merging.

Core validation, context propagation, audit logging, dashboard handling, translated requests, passthrough requests, and websocket realtime paths look consistent. The score is limited by the missing mutation application in realtime HTTP signaling requests.

internal/server/realtime_webrtc_service.go

T-Rex T-Rex Logs

What T-Rex did

  • Reproduced realtime mutations by running a focused Go repro test against internal/server.forwardRealtimeHTTP with an httptest upstream and a context-scoped core.HeaderMutation, which showed the upstream path responding HTTP 200 OK and the mutation not being applied (X-Repro-Mutation absent).
  • Go test suites covering header mutation and guardrails completed, with the gomodel/internal/core header mutation tests and guardrails tests passing.
  • The Node.js dashboard test run completed, with the dashboard test suite reporting 56 tests and all passing.
  • Lint tooling and pre-commit checks could not run locally because golangci-lint is missing, blocking full tooling coverage.

View all artifacts

T-Rex Ran code and verified through T-Rex

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant Server as Echo middleware
participant Workflow as Workflow service
participant Guardrail as header_modification step
participant Audit as Audit revision chain
participant Builder as Outbound request builder
participant Provider

Client->>Server: inbound model request with headers
Server->>Workflow: resolve request workflow
Workflow-->>Server: active guardrail pipeline
Server->>Guardrail: evaluate inbound headers
Guardrail-->>Server: HeaderMutation set/remove delta
Server->>Audit: append request_revision header delta
Server->>Builder: store mutation in request context
Builder->>Builder: apply mutation after provider defaults/client passthrough headers
Builder->>Provider: outbound provider request with modified headers
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant Server as Echo middleware
participant Workflow as Workflow service
participant Guardrail as header_modification step
participant Audit as Audit revision chain
participant Builder as Outbound request builder
participant Provider

Client->>Server: inbound model request with headers
Server->>Workflow: resolve request workflow
Workflow-->>Server: active guardrail pipeline
Server->>Guardrail: evaluate inbound headers
Guardrail-->>Server: HeaderMutation set/remove delta
Server->>Audit: append request_revision header delta
Server->>Builder: store mutation in request context
Builder->>Builder: apply mutation after provider defaults/client passthrough headers
Builder->>Provider: outbound provider request with modified headers
Loading

Reviews (1): Last reviewed commit: "feat(guardrails): support outbound heade..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/gomodel/docs/docs.go`:
- Line 6279: Update the source annotation that generates the RequestRevisions
description to describe these as request-processing changes rather than ingress
changes, while preserving the existing explanation of body rewrites and outbound
header modifications. Then regenerate the Swagger output so the generated
documentation matches the updated annotation.

In `@internal/admin/dashboard/static/css/dashboard.css`:
- Around line 5272-5278: Add a .guardrail-action-row modifier next to
.guardrail-header-row in internal/admin/dashboard/static/css/dashboard.css,
swapping the first two grid column sizing rules for the action-row DOM order.
Apply this modifier alongside the existing class on the action row container in
internal/admin/dashboard/templates/page-guardrails.html at line 152.

In `@internal/server/header_modification.go`:
- Around line 9-10: Replace the incorrect gomodel import prefix with
github.com/enterpilot/gomodel in internal/server/header_modification.go (lines
9-10) and internal/server/header_modification_test.go (lines 12-13). Make no
direct change to internal/workflows/service.go (lines 344-353); after updating
the imports, check internal/guardrails/header_modification.go for any remaining
gomodel/internal imports and re-run the build.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 57d344be-bca8-4dbd-86a6-9cdd2518bc12

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc918e and 837dc54.

📒 Files selected for processing (32)
  • CLAUDE.md
  • cmd/gomodel/docs/docs.go
  • config/config.example.yaml
  • config/guardrails.go
  • docs/advanced/guardrails.mdx
  • docs/openapi.json
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/modules/audit-list.js
  • internal/admin/dashboard/static/js/modules/audit-list.test.cjs
  • internal/admin/dashboard/static/js/modules/guardrails.js
  • internal/admin/dashboard/static/js/modules/guardrails.test.cjs
  • internal/admin/dashboard/templates/page-guardrails.html
  • internal/app/app.go
  • internal/app/app_test.go
  • internal/auditlog/auditlog.go
  • internal/core/header_mutation.go
  • internal/core/header_mutation_test.go
  • internal/guardrails/definitions.go
  • internal/guardrails/header_modification.go
  • internal/guardrails/header_modification_test.go
  • internal/llmclient/client.go
  • internal/llmclient/client_test.go
  • internal/responsecache/semantic.go
  • internal/responsecache/semantic_test.go
  • internal/server/header_modification.go
  • internal/server/header_modification_test.go
  • internal/server/http.go
  • internal/server/internal_chat_completion_executor.go
  • internal/server/internal_chat_completion_executor_test.go
  • internal/server/passthrough_support.go
  • internal/server/realtime_service_test.go
  • internal/workflows/service.go

Comment thread cmd/gomodel/docs/docs.go
},
"request_revisions": {
"description": "RequestRevisions captures the ingress request-rewrite chain: one entry\nper registered rewriter that changed the body, in application order.\nRequestBody always remains the original client request; the last\nrevision is what was forwarded downstream.",
"description": "RequestRevisions captures intended ingress changes in application order:\nbody rewrites and outbound header modifications. RequestBody always\nremains the original client request. A revision records what would be\napplied if execution reaches provider egress; it is not proof of egress.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Describe these as request-processing changes, not ingress changes.

This description groups outbound provider-header mutations under “ingress changes,” which is directionally misleading and can confuse audit consumers. Update the generator/source annotation and regenerate the Swagger file.

Proposed wording
-                    "description": "RequestRevisions captures intended ingress changes in application order:\nbody rewrites and outbound header modifications. RequestBody always\nremains the original client request. A revision records what would be\napplied if execution reaches provider egress; it is not proof of egress.",
+                    "description": "RequestRevisions captures intended request-processing changes in application order:\nbody rewrites and outbound header modifications. RequestBody always\nremains the original client request. A revision records what would be\napplied if execution reaches provider egress; it is not proof of egress.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"description": "RequestRevisions captures intended ingress changes in application order:\nbody rewrites and outbound header modifications. RequestBody always\nremains the original client request. A revision records what would be\napplied if execution reaches provider egress; it is not proof of egress.",
"description": "RequestRevisions captures intended request-processing changes in application order:\nbody rewrites and outbound header modifications. RequestBody always\nremains the original client request. A revision records what would be\napplied if execution reaches provider egress; it is not proof of egress.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/gomodel/docs/docs.go` at line 6279, Update the source annotation that
generates the RequestRevisions description to describe these as
request-processing changes rather than ingress changes, while preserving the
existing explanation of body rewrites and outbound header modifications. Then
regenerate the Swagger output so the generated documentation matches the updated
annotation.

Comment on lines +5272 to +5278
.guardrail-header-row {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(120px, auto) minmax(0, 1fr) auto;
gap: 8px;
align-items: center;
width: 100%;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix visual layout glitch on action rows.

The CSS grid defined in .guardrail-header-row is designed for the DOM order of the condition row ([Header input] [Select] [Value input]). Because the action row has a different element order ([Select] [Header input] [Value input]), reusing the same unmodified class causes the Select dropdown to stretch excessively (1.1fr) while the Header input becomes rigidly constrained (minmax(120px, auto)).

  • internal/admin/dashboard/static/css/dashboard.css#L5272-L5278: Add a .guardrail-action-row modifier class with swapped column sizes.
  • internal/admin/dashboard/templates/page-guardrails.html#L152-L152: Apply the modifier class to the action row container.
✨ Proposed CSS and HTML fix

In internal/admin/dashboard/static/css/dashboard.css:

 .guardrail-header-row {
   display: grid;
   grid-template-columns: minmax(0, 1.1fr) minmax(120px, auto) minmax(0, 1fr) auto;
   gap: 8px;
   align-items: center;
   width: 100%;
 }
+
+.guardrail-action-row {
+  grid-template-columns: minmax(120px, auto) minmax(0, 1.1fr) minmax(0, 1fr) auto;
+}

In internal/admin/dashboard/templates/page-guardrails.html:

                                     <template x-if="field.input === 'header_actions'">
                                         <div class="guardrail-header-rows">
                                             <template x-for="(row, index) in guardrailHeaderRows(field)" :key="field.key + '-' + index">
-                                                <div class="guardrail-header-row">
+                                                <div class="guardrail-header-row guardrail-action-row">
                                                     <select class="form-select settings-select" :aria-label="'Action ' + (index + 1) + ' kind'" `@change`="setGuardrailActionMode(field, index, $event.target.value)">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.guardrail-header-row {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(120px, auto) minmax(0, 1fr) auto;
gap: 8px;
align-items: center;
width: 100%;
}
.guardrail-header-row {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(120px, auto) minmax(0, 1fr) auto;
gap: 8px;
align-items: center;
width: 100%;
}
.guardrail-action-row {
grid-template-columns: minmax(120px, auto) minmax(0, 1.1fr) minmax(0, 1fr) auto;
}
📍 Affects 2 files
  • internal/admin/dashboard/static/css/dashboard.css#L5272-L5278 (this comment)
  • internal/admin/dashboard/templates/page-guardrails.html#L152-L152
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/admin/dashboard/static/css/dashboard.css` around lines 5272 - 5278,
Add a .guardrail-action-row modifier next to .guardrail-header-row in
internal/admin/dashboard/static/css/dashboard.css, swapping the first two grid
column sizing rules for the action-row DOM order. Apply this modifier alongside
the existing class on the action row container in
internal/admin/dashboard/templates/page-guardrails.html at line 152.

Comment on lines +9 to +10
"gomodel/internal/auditlog"
"gomodel/internal/core"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Wrong module import prefix (gomodel/...) breaks the build across the header-modification feature. CI directly fails on the two files that import "gomodel/internal/auditlog" / "gomodel/internal/core"; the real module path is github.com/enterpilot/gomodel (per the Docker image enterpilot/gomodel, the ENTERPILOT/GoModel repo, and the github.com/enterpilot/gomodel/internal/core reference in the workflows/service.go build error). The workflows/service.go failure looks like a downstream symptom of the same wrong-prefix import existing in a dependency (most likely internal/guardrails/header_modification.go, which defines Pipeline.HeaderMutators() and is not included in this review batch).

  • internal/server/header_modification.go#L9-L10: change "gomodel/internal/auditlog" and "gomodel/internal/core" to "github.com/enterpilot/gomodel/internal/auditlog" and "github.com/enterpilot/gomodel/internal/core".
  • internal/server/header_modification_test.go#L12-L13: apply the same import-path fix.
  • internal/workflows/service.go#L344-L353: no code change needed here; re-run the build after fixing the imports above and additionally check internal/guardrails/header_modification.go's imports for the same gomodel/... prefix, since that is the most likely source of the []invalid type mismatch reported at this site.
🐛 Verify remaining occurrences of the wrong import prefix
#!/bin/bash
rg -n '"gomodel/internal' --type=go
rg -n '"github.com/enterpilot/gomodel/internal' --type=go | head -5
cat go.mod | head -3
🧰 Tools
🪛 GitHub Check: Build

[failure] 9-9:
package gomodel/internal/auditlog is not in std (/opt/hostedtoolcache/go/1.26.5/x64/src/gomodel/internal/auditlog)

🪛 GitHub Check: Vulnerability Scan

[failure] 10-10:
could not import gomodel/internal/core (invalid package name: "")


[failure] 9-9:
could not import gomodel/internal/auditlog (invalid package name: "")


[failure] 9-9:
package gomodel/internal/auditlog is not in std (/opt/hostedtoolcache/go/1.26.5/x64/src/gomodel/internal/auditlog)

📍 Affects 3 files
  • internal/server/header_modification.go#L9-L10 (this comment)
  • internal/server/header_modification_test.go#L12-L13
  • internal/workflows/service.go#L344-L353
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/server/header_modification.go` around lines 9 - 10, Replace the
incorrect gomodel import prefix with github.com/enterpilot/gomodel in
internal/server/header_modification.go (lines 9-10) and
internal/server/header_modification_test.go (lines 12-13). Make no direct change
to internal/workflows/service.go (lines 344-353); after updating the imports,
check internal/guardrails/header_modification.go for any remaining
gomodel/internal imports and re-run the build.

Source: Linters/SAST tools

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.

1 participant