Skip to content

feat(config): make user path header configurable#330

Merged
SantiagoDePolonia merged 2 commits into
mainfrom
feat/user-path-header-configurable
May 12, 2026
Merged

feat(config): make user path header configurable#330
SantiagoDePolonia merged 2 commits into
mainfrom
feat/user-path-header-configurable

Conversation

@SantiagoDePolonia
Copy link
Copy Markdown
Contributor

@SantiagoDePolonia SantiagoDePolonia commented May 12, 2026

Summary

  • add USER_PATH_HEADER / server.user_path_header with the default preserved as X-GoModel-User-Path
  • use the configured header across ingress capture, auth-key user path overrides, audit/internal headers, and passthrough filtering
  • document the setting in .env.template, config examples, Mintlify docs, README, dashboard help text, and Helm values

Verification

  • go test ./cmd/... ./internal/... ./config/...
  • make test-dashboard
  • go test -run '^TestHotPathPerfGuard$' -count=1 -v ./tests/perf/...
  • PATH=/Users/jakubaw/.nvm/versions/node/v20.20.0/bin:/Users/jakubaw/.codex/tmp/arg0/codex-arg0vaQaLD:/Users/jakubaw/.nvm/versions/node/v24.14.1/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/path:/Users/jakubaw/Downloads/google-cloud-sdk/bin:/Users/jakubaw/.opencode/bin:/Users/jakubaw/.antigravity/antigravity/bin:/Users/jakubaw/.codeium/windsurf/bin:/opt/homebrew/Cellar/pyenv-virtualenv/1.4.0/shims:/Users/jakubaw/.pyenv/shims:/opt/homebrew/opt/mysql-client@8.4/bin:/Users/jakubaw/.nvm/versions/node/v24.14.1/bin:/Library/Frameworks/Python.framework/Versions/3.10/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/go/bin:/Users/jakubaw/Library/Python/3.10/bin:/Users/jakubaw/.local/bin:/Users/jakubaw/Library/Python/3.10/bin:/Users/jakubaw/.local/bin npx -y mint@latest validate
  • PATH=/Users/jakubaw/.nvm/versions/node/v20.20.0/bin:/Users/jakubaw/.codex/tmp/arg0/codex-arg0vaQaLD:/Users/jakubaw/.nvm/versions/node/v24.14.1/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/path:/Users/jakubaw/Downloads/google-cloud-sdk/bin:/Users/jakubaw/.opencode/bin:/Users/jakubaw/.antigravity/antigravity/bin:/Users/jakubaw/.codeium/windsurf/bin:/opt/homebrew/Cellar/pyenv-virtualenv/1.4.0/shims:/Users/jakubaw/.pyenv/shims:/opt/homebrew/opt/mysql-client@8.4/bin:/Users/jakubaw/.nvm/versions/node/v24.14.1/bin:/Library/Frameworks/Python.framework/Versions/3.10/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/go/bin:/Users/jakubaw/Library/Python/3.10/bin:/Users/jakubaw/.local/bin:/Users/jakubaw/Library/Python/3.10/bin:/Users/jakubaw/.local/bin npx -y mint@latest broken-links
  • helm dependency build ./helm && helm template gomodel ./helm --set providers.openai.apiKey=sk-test

Summary by CodeRabbit

  • New Features

    • User-path scoping is now configurable via USER_PATH_HEADER / server.user_path_header (default: X-GoModel-User-Path).
  • Documentation

    • Configuration guides, advanced docs, ADRs, and admin UI wording updated to describe the configurable user-path header and its behavior.
  • Chores

    • Deployment charts and Helm values updated to expose the new header setting as an environment variable.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 14f76899-7557-49d4-8650-eb4da9bae262

📥 Commits

Reviewing files that changed from the base of the PR and between 99b079f and f3ff309.

📒 Files selected for processing (3)
  • internal/core/context.go
  • internal/core/user_path_test.go
  • internal/server/passthrough_support.go

📝 Walkthrough

Walkthrough

This PR adds configurability to the HTTP header name used to carry user-path request boundaries. The default remains X-GoModel-User-Path, but deployments can now override it via the USER_PATH_HEADER environment variable or server.user_path_header YAML configuration. The configured header name is threaded through the middleware pipeline and downstream request processing via context and function parameters, ensuring consistent header usage in authentication, snapshots, auditing, and passthrough filtering.

Changes

User-Path Header Configurability

Layer / File(s) Summary
Configuration, validation, and normalization
config/server.go, config/config.go, config/config_test.go, config/config.example.yaml
ServerConfig adds UserPathHeader field. New NormalizeHeaderName utility trims, validates header-name format, and canonicalizes using textproto.CanonicalMIMEHeaderKey. Config loading applies normalization with error handling; tests verify defaults, YAML override, env-var override, and rejection of invalid names.
Core context and snapshot abstractions
internal/core/user_path.go, internal/core/context.go, internal/core/request_snapshot.go
Context key and WithUserPathHeaderName helper store configured header names in request scope. UserPathHeaderName and UserPathHeaderNameFromContext normalize and retrieve header names with fallback to default. RequestSnapshot.WithUserPathHeader method allows header-agnostic snapshot header updates; tests validate header-name helpers.
Request snapshot capture middleware
internal/server/request_snapshot.go, internal/server/request_snapshot_test.go
RequestSnapshotCapture now accepts optional header name, derives effective header, normalizes incoming request value from that header, stores header name in context, and applies validation with configurable error reporting. Test verifies custom header capture and context storage.
Authentication middleware with configurable header
internal/server/auth.go, internal/server/auth_test.go
AuthMiddlewareWithAuthenticator accepts optional header name, uses configured header for authenticated user-path writing and snapshot enrichment instead of hardcoded default. Test verifies custom header is used while default header remains unset.
Server initialization and middleware wiring
internal/server/http.go, internal/app/app.go, internal/server/base_path.go
Server Config field carries configured header name. Server New derives userPathHeaderName and passes it to both request snapshot and authentication middleware. App.New wires config value into server initialization. Helper configuredUserPathHeader selects between default and derived header names.
Downstream header handling in audit and passthrough
internal/auditlog/entry_capture.go, internal/server/passthrough_support.go, internal/server/passthrough_support_test.go
Audit logging uses context-derived header name instead of fixed constant. Passthrough header filtering uses context-derived name and skips both default header and any configured override. Test verifies configured header and default are excluded while other headers pass through.
Documentation, UI templates, and infrastructure configuration
.env.template, CLAUDE.md, README.md, docs/advanced/*.mdx, docs/features/user-path.mdx, helm/*, docs/adr/*, internal/admin/dashboard/templates/*, internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs
Environment templates and user guides document USER_PATH_HEADER env var and server.user_path_header YAML option with default value. Dashboard templates and tests update UI help text to reference configured user path request header generically. Helm charts add values, schema, ConfigMap, and deployment environment variable support.

Sequence Diagram

The PR does not introduce a new multi-component flow to visualize—instead, it makes the header name configurable by threading it through existing middleware layers. Configuration loading, context storage, and middleware parameter passing are linear paths that are self-evident from the code structure, so a sequence diagram would not add clarity.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ENTERPILOT/GoModel#189: Adds user-path request boundary scoping; related changes to header handling and request snapshot plumbing.
  • ENTERPILOT/GoModel#197: Modifies user-path handling in auth, snapshots, and admin UI; touches the same code paths.

Suggested labels

release:internal

🐰 I hopped through headers, neat and sly,
From hardcoded text to names you supply.
Context now holds it, middleware sings,
User-paths flow where flexibility brings.
A cheerful rabbit applauds config's new sky.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.59% 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 accurately describes the primary change: making the user path header configurable via a new USER_PATH_HEADER configuration option with default X-GoModel-User-Path.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/user-path-header-configurable

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 and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 12, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 91.42857% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
config/server.go 77.77% 1 Missing and 1 partial ⚠️
internal/app/app.go 0.00% 1 Missing ⚠️
internal/core/request_snapshot.go 80.00% 1 Missing ⚠️
internal/server/passthrough_support.go 90.00% 0 Missing and 1 partial ⚠️
internal/server/request_snapshot.go 90.90% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@docs/adr/0006-semantic-response-cache.md`:
- Line 54: Replace the absolute GitHub URL in the ADR text with a relative
repository link so the doc remains valid offline; specifically update the link
pointing to internal/responsecache/vecstore.go in
docs/adr/0006-semantic-response-cache.md to use a relative path (e.g.,
../../internal/responsecache/vecstore.go) while keeping the surrounding text
about `vector_store.type` unchanged.
🪄 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: ea55637a-c6c8-4336-9c3d-97d6f30f6f33

📥 Commits

Reviewing files that changed from the base of the PR and between cd05646 and 99b079f.

📒 Files selected for processing (34)
  • .env.template
  • CLAUDE.md
  • README.md
  • config/config.example.yaml
  • config/config.go
  • config/config_test.go
  • config/server.go
  • docs/adr/0002-ingress-frame-and-semantic-envelope.md
  • docs/adr/0006-semantic-response-cache.md
  • docs/advanced/config-yaml.mdx
  • docs/advanced/configuration.mdx
  • docs/advanced/workflows.mdx
  • docs/features/user-path.mdx
  • helm/README.md
  • helm/templates/configmap.yaml
  • helm/templates/deployment.yaml
  • helm/values.schema.json
  • helm/values.yaml
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs
  • internal/admin/dashboard/templates/page-auth-keys.html
  • internal/admin/dashboard/templates/page-models.html
  • internal/app/app.go
  • internal/auditlog/entry_capture.go
  • internal/core/context.go
  • internal/core/request_snapshot.go
  • internal/core/user_path.go
  • internal/server/auth.go
  • internal/server/auth_test.go
  • internal/server/base_path.go
  • internal/server/http.go
  • internal/server/passthrough_support.go
  • internal/server/passthrough_support_test.go
  • internal/server/request_snapshot.go
  • internal/server/request_snapshot_test.go

### Vector Store

`VecStore` interface + `type`-switched factory in [`internal/responsecache/vecstore.go`](../../internal/responsecache/vecstore.go). When semantic caching is enabled, **`vector_store.type` is required** (no default). Supported values:
`VecStore` interface + `type`-switched factory in [`internal/responsecache/vecstore.go`](https://github.com/ENTERPILOT/GoModel/blob/main/internal/responsecache/vecstore.go). When semantic caching is enabled, **`vector_store.type` is required** (no default). Supported values:
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 12, 2026

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if the referenced file exists in the repository

# Test: Verify the file exists at the expected path
fd -t f '^vecstore\.go$' internal/responsecache/

Repository: ENTERPILOT/GoModel

Length of output: 97


Use a relative path for better documentation maintainability.

For markdown documentation, relative paths are more robust than absolute GitHub URLs to main. They work immediately and remain valid when viewing files offline or in local repositories.

📝 Suggested change
-`VecStore` interface + `type`-switched factory in [`internal/responsecache/vecstore.go`](https://github.com/ENTERPILOT/GoModel/blob/main/internal/responsecache/vecstore.go). When semantic caching is enabled, **`vector_store.type` is required** (no default). Supported values:
+`VecStore` interface + `type`-switched factory in [`internal/responsecache/vecstore.go`](../../internal/responsecache/vecstore.go). When semantic caching is enabled, **`vector_store.type` is required** (no default). Supported values:
📝 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
`VecStore` interface + `type`-switched factory in [`internal/responsecache/vecstore.go`](https://github.com/ENTERPILOT/GoModel/blob/main/internal/responsecache/vecstore.go). When semantic caching is enabled, **`vector_store.type` is required** (no default). Supported values:
`VecStore` interface + `type`-switched factory in [`internal/responsecache/vecstore.go`](../../internal/responsecache/vecstore.go). When semantic caching is enabled, **`vector_store.type` is required** (no default). Supported values:
🤖 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 `@docs/adr/0006-semantic-response-cache.md` at line 54, Replace the absolute
GitHub URL in the ADR text with a relative repository link so the doc remains
valid offline; specifically update the link pointing to
internal/responsecache/vecstore.go in docs/adr/0006-semantic-response-cache.md
to use a relative path (e.g., ../../internal/responsecache/vecstore.go) while
keeping the surrounding text about `vector_store.type` unchanged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am leaving this as an absolute GitHub URL because the relative repository path makes mint broken-links fail from the Mintlify docs root: ../../internal/responsecache/vecstore.go resolves outside the docs site and is reported as broken. The absolute URL keeps the ADR link valid in the published docs and keeps the docs verification green.

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.

@SantiagoDePolonia, that's a completely valid constraint — when Mintlify's broken-links checker resolves links relative to the docs site root, a path like ../../internal/responsecache/vecstore.go correctly falls outside the published site and is flagged as broken. Using the absolute GitHub URL is the right call here. I'll withdraw the suggestion.


✏️ Learnings added
Learnt from: SantiagoDePolonia
Repo: ENTERPILOT/GoModel PR: 330
File: docs/adr/0006-semantic-response-cache.md:54-54
Timestamp: 2026-05-12T16:27:27.727Z
Learning: In the GoModel repository, ADR and other markdown files under the `docs/` directory that link to source files outside the docs tree (e.g. `internal/`) must use absolute GitHub URLs (e.g. `https://github.com/ENTERPILOT/GoModel/blob/main/internal/...`) rather than relative paths. Relative paths like `../../internal/...` resolve outside the Mintlify docs site root and are reported as broken by `npx mintlatest broken-links`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 12, 2026

Greptile Summary

This PR makes the user-path HTTP header configurable via USER_PATH_HEADER env var or server.user_path_header YAML key (defaulting to X-GoModel-User-Path), and propagates the configured name consistently through ingress capture, auth-key overrides, audit headers, and passthrough filtering.

  • The header name is normalized at config-load time, stored per-request in the context, and read by all relevant middleware — ingress snapshot capture, auth middleware, audit log capture, and passthrough header filtering — each using UserPathHeaderNameFromContext or the closed-over header name.
  • Documentation is updated across README, env template, YAML example, Mintlify docs, Helm values/schema, and dashboard help text; test coverage extends to the new config validation, env/YAML override precedence, and the middleware-integration path.

Confidence Score: 4/5

Safe to merge — the configurable header is propagated consistently through all relevant middleware layers, defaults are preserved, and the new config path is validated at startup.

The change touches ingress capture, auth middleware, audit headers, passthrough filtering, Helm charts, and documentation. Each layer correctly reads the header name from either the server config or the request context. The two style observations have no impact on runtime correctness with the current callers, and test coverage for config loading, middleware integration, and passthrough filtering is solid.

No files require special attention, though internal/core/context.go (WithUserPathHeaderName) is worth a second read given its silent no-op behaviour when the default header is supplied.

Important Files Changed

Filename Overview
config/server.go Adds UserPathHeader field to ServerConfig and NormalizeHeaderName with RFC 7230 tchar regex validation; default fallback path and canonical MIME normalization are both correct.
internal/core/context.go Adds userPathHeaderNameKey context key and WithUserPathHeaderName / UserPathHeaderNameFromContext helpers; the default-bypass optimization is correct for current callers but slightly surprising semantically.
internal/server/request_snapshot.go Changes RequestSnapshotCapture to accept the configured header name as a variadic arg, reads the user path from it, and stores the header name in the context; implementation is correct.
internal/server/auth.go Auth middleware extended with variadic userPathHeader arg; when an auth key supplies a user path, the request header, context, and snapshot are all updated using the configured name.
internal/server/passthrough_support.go Both the hardcoded default header and the runtime-configured header are filtered from passthrough requests; the empty-canonical-key guard placed after the user-path checks is harmless but slightly out of logical order.
internal/auditlog/entry_capture.go Audit header emission correctly switches from the hardcoded constant to UserPathHeaderNameFromContext, so internal audit traffic carries the operator-configured header name.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[HTTP Request arrives] --> B[RequestSnapshotCapture]
    B --> C{IngressManaged endpoint?}
    C -- No --> D[pass through unchanged]
    C -- Yes --> E[Read user path from configured header name]
    E --> F[Store headerName in request context]
    F --> G[Build RequestSnapshot]
    G --> H[AuthMiddleware]
    H --> I{Auth key has user_path?}
    I -- No --> J[proceed]
    I -- Yes --> K[Override effective user path, update snapshot headers, set request header]
    K --> J
    J --> L[AuditLog]
    L --> M[UserPathHeaderNameFromContext to emit correct header name]
    J --> N[PassthroughFilter]
    N --> O[Skip default header, skip configured header, forward everything else]
Loading

Comments Outside Diff (1)

  1. internal/server/passthrough_support.go, line 125-139 (link)

    P2 buildPassthroughHeaders already computes the canonical key before calling this function, so the http.CanonicalHeaderKey calls inside skipPassthroughRequestHeader are redundant on the hot path. Additionally, the empty-string guard (canonicalKey == "") logically belongs at the top of the function — an empty key can never match any named header, so checking it after the user-path loops wastes work and buries a defensive guard where it looks like an afterthought.

Reviews (1): Last reviewed commit: "feat(config): make user path header conf..." | Re-trigger Greptile

Comment thread internal/core/context.go
Comment on lines +150 to +156
func WithUserPathHeaderName(ctx context.Context, headerName string) context.Context {
headerName = UserPathHeaderName(headerName)
if headerName == UserPathHeader {
return ctx
}
return context.WithValue(ctx, userPathHeaderNameKey, headerName)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 WithUserPathHeaderName silently no-ops when given the default, but doesn't clear existing values

When headerName normalizes to UserPathHeader, the function returns ctx unchanged. If ctx already carries a non-default header name (written by a prior middleware layer or test helper), calling WithUserPathHeaderName(ctx, UserPathHeader) will leave that stale name in the context rather than resetting it to the default. No current callsite exercises this path because the server-wide configuration is constant, but the API contract is surprising for callers who expect the write to be unconditional. A brief doc comment noting that "calling with the default header name is a no-op and does not clear any existing value" would prevent confusion in future use.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented May 12, 2026

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

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview May 12, 2026, 4:44 PM

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

@SantiagoDePolonia SantiagoDePolonia merged commit ddd80ae into main May 12, 2026
18 checks passed
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