Skip to content

Conversation

@akshaydeo
Copy link
Contributor

@akshaydeo akshaydeo commented Dec 26, 2025

Summary

Add configurable header filtering for extra headers forwarded to LLM providers via the x-bf-eh-* prefix. This allows administrators to control which headers are allowed to pass through to providers using allowlist and denylist mechanisms.

Changes

  • Added GlobalHeaderFilterConfig struct with allowlist and denylist fields
  • Integrated header filtering into the context conversion process
  • Added API endpoints for getting and updating header filter configuration
  • Created UI for managing header filter settings with clear documentation
  • Updated hash generation to include header filter configuration
  • Implemented filter logic with proper precedence rules:
    • If allowlist is non-empty, only headers in allowlist are forwarded
    • If denylist is non-empty, headers in denylist are blocked
    • If both are configured, allowlist takes precedence first, then denylist filters
    • If neither is configured, all headers pass through (except security-sensitive ones)

Type of change

  • Feature
  • Bug fix
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (Next.js)
  • Docs

How to test

  1. Start Bifrost server
  2. Navigate to the new Header Filter page in the UI (under Config)
  3. Add headers to allowlist/denylist and save changes
  4. Test API requests with x-bf-eh-* headers to verify filtering works:
# Test with a header that should be allowed
curl -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "x-bf-eh-allowed-header: test-value" \
  -d '{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Hello"}]}'

# Test with a header that should be blocked
curl -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "x-bf-eh-blocked-header: test-value" \
  -d '{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Hello"}]}'

Screenshots/Recordings

N/A

Breaking changes

  • Yes
  • No

Related issues

N/A

Security considerations

  • Security-sensitive headers are always blocked regardless of configuration
  • The implementation normalizes header names to lowercase for case-insensitive matching
  • The feature helps prevent unintended header leakage to third-party providers

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 26, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Configurable global header filtering with allowlist and denylist to control which headers are forwarded.
  • UI

    • New Header Forwarding section in settings to view and edit allowlist/denylist with add/remove/edit controls and inline help.
  • Configuration

    • Header filter settings added to configuration schema and persisted; defaults provided.
  • Operational

    • Runtime updates to header filter config apply dynamically and trigger reloads without full server restarts; failures are logged.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds a global header filtering feature: new schema and types (allowlist/denylist), DB persistence and store APIs, runtime loading and hot-reload, request-context filtering integration (ConvertToBifrostContext), UI/schema/types for editing, and test/mock updates.

Changes

Cohort / File(s) Summary
Config Type & Key
framework/configstore/tables/config.go
Added ConfigHeaderFilterKey constant and GlobalHeaderFilterConfig struct (Allowlist, Denylist).
Client Config & Hashing
framework/configstore/clientconfig.go
Added HeaderFilterConfig field to ClientConfig; extended GenerateClientConfigHash to include sorted Allowlist/Denylist.
ConfigStore Interface
framework/configstore/store.go
Added GetHeaderFilterConfig(ctx context.Context) (*tables.GlobalHeaderFilterConfig, error) and UpdateHeaderFilterConfig(ctx context.Context, config *tables.GlobalHeaderFilterConfig) error.
RDB Implementation
framework/configstore/rdb.go
Implemented GetHeaderFilterConfig and UpdateHeaderFilterConfig on RDBConfigStore to read/write JSON under header filter key. Note: duplicate insertion caused duplicate method declarations in the file.
Runtime Config Loading & Accessors
transports/bifrost-http/lib/config.go
Added GetHeaderFilterConfig() on Config; added loadDefaultHeaderFilterConfig() and merge/sync logic to load and persist header filter config during startup/merge.
Context Creation & Header Filtering
transports/bifrost-http/lib/ctx.go
Extended ConvertToBifrostContext(..., headerFilterConfig) with shouldAllowHeader() and securityDenylist; gating forwarded headers by allowlist/denylist and preserving security blocks.
Handler Interfaces & Reload Path
transports/bifrost-http/handlers/config.go
Added ReloadHeaderFilterConfig(ctx context.Context, config *configstoreTables.GlobalHeaderFilterConfig) error to ConfigManager; getConfig exposes header filter config; updateConfig detects changes via headerFilterConfigEqual and triggers reload (500 on reload failure).
Server Hot-Reload
transports/bifrost-http/server/server.go
Added ReloadHeaderFilterConfig to ServerCallbacks and BifrostHTTPServer to update in-memory ClientConfig.HeaderFilterConfig and log allow/deny counts.
Call-site Propagation
transports/bifrost-http/handlers/inference.go, transports/bifrost-http/handlers/mcp.go, transports/bifrost-http/integrations/router.go
Updated all ConvertToBifrostContext call sites to pass GetHeaderFilterConfig() as third argument.
HandlerStore API & Mocks
transports/bifrost-http/lib/config.go, transports/bifrost-http/integrations/bedrock_test.go
Added HandlerStore.GetHeaderFilterConfig() to HandlerStore and implemented Config.GetHeaderFilterConfig(); tests updated with mock headerFilterConfig field and accessor.
Schema & UI Types
transports/config.schema.json, ui/lib/types/config.ts, ui/lib/types/schemas.ts
Added header_filter_config JSON schema; TypeScript GlobalHeaderFilterConfig, DefaultGlobalHeaderFilterConfig, and Zod schemas; extended BifrostConfig/CoreConfig types.
Configuration UI
ui/app/workspace/config/views/clientSettingsView.tsx
Added Header Forwarding UI: allowlist/denylist editors, headerFilterConfigEqual helper, RBAC-aware controls, sanitization before save, and local state wiring to new config.

Sequence Diagram(s)

sequenceDiagram
  participant UI as UI (ClientSettings)
  participant API as Bifrost HTTP API
  participant Server as BifrostHTTPServer
  participant Store as ConfigStore (DB)
  Note over UI,API: Admin updates header_filter_config via UI
  UI->>API: PUT /api/config (header_filter_config)
  API->>Server: updateConfig(...) with new header_filter_config
  alt config changed
    Server->>Server: call ReloadHeaderFilterConfig(ctx, config)
    Server->>Store: UpdateHeaderFilterConfig(ctx, config)
    Store-->>Server: OK
    Server-->>API: 200 OK
  else no change
    Server-->>API: 200 OK (no reload)
  end

  Note over RequestFlow: Incoming request uses current config
  participant Req as Client Request
  Req->>API: HTTP request
  API->>Server: ConvertToBifrostContext(ctx, allowDirectKeys, headerFilterConfig)
  Server->>Server: shouldAllowHeader() checks (allowlist/denylist + securityDenylist)
  Server-->>Req: process request with filtered forwarded headers
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hop through headers, tidy and spry,
Allowlists and denylists waving goodbye.
From schema to DB and each routed request,
Filters now hum — the stack feels blessed.
Hop, hop — configuration handled with zest!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'adds allowlist and denylist for headers' is concise, clear, and accurately describes the primary feature being added in the changeset.
Description check ✅ Passed The PR description comprehensively covers all template sections including summary, changes, type of change, affected areas, testing instructions, security considerations, and required checklist items.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 12-26-adds_allowlist_and_denylist_for_headers

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

Copy link
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@akshaydeo akshaydeo marked this pull request as ready for review December 26, 2025 08:15
Copy link
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: 0

🧹 Nitpick comments (6)
transports/bifrost-http/lib/config.go (1)

39-41: Expose header filter via HandlerStore; consider future-proofing concurrent access

Wiring GetHeaderFilterConfig through HandlerStore and implementing it on Config by returning c.ClientConfig.HeaderFilterConfig gives the router/context code clean read-only access, and mirrors the existing ShouldAllowDirectKeys pattern.

If you expect frequent runtime reloads of the header filter config, it may be worth eventually backing HeaderFilterConfig with an atomic.Pointer or guarding writes with the existing mutex to keep the race detector quiet while still allowing cheap reads from hot paths. Not a blocker given the existing pattern, just something to consider.

Also applies to: 2065-2071

transports/config.schema.json (1)

84-103: Clarify header_filter_config header name format (case and prefix).

Runtime filtering (in ConvertToBifrostContext) lowercases header names and strips the x-bf-eh- prefix before checking allowlist/denylist, but this schema’s descriptions don’t say that. If users configure "User-Agent" or include the prefix, those entries will never match the normalized "user-agent" label, and filtering will appear ineffective.

Consider either:

  • documenting explicitly that entries must be lowercase header names without the x-bf-eh- prefix (e.g. "user-agent"), or
  • normalizing the configured allowlist/denylist to lowercase at load time so configuration is case-insensitive.

This will avoid subtle misconfigurations and align user expectations with the actual filter behavior.

ui/lib/types/schemas.ts (1)

680-691: Consider normalizing and deduplicating header names in the Zod schemas.

The schemas correctly mirror the backend shape, but you may want to harden UX here:

  • Normalize to lowercase so config is case-insensitive and matches the runtime behavior that already lowercases header names before filtering.
  • Optionally deduplicate entries to avoid redundant values.

For example:

export const globalHeaderFilterConfigSchema = z.object({
  allowlist: z
    .array(z.string().min(1))
    .transform(list => Array.from(new Set(list.map(h => h.toLowerCase()))))
    .optional(),
  denylist: z
    .array(z.string().min(1))
    .transform(list => Array.from(new Set(list.map(h => h.toLowerCase()))))
    .optional(),
});

This keeps the API the same but makes misconfiguration much harder.

Also applies to: 709-710

transports/bifrost-http/handlers/inference.go (1)

449-451: Header filter wiring into ConvertToBifrostContext is correct but quite repetitive.

All the updated call sites correctly pass h.handlerStore.ShouldAllowDirectKeys() and h.config.GetHeaderFilterConfig() into ConvertToBifrostContext, so behavior-wise this looks good.

Given how often this pattern appears, consider adding a small helper on CompletionHandler, e.g.:

func (h *CompletionHandler) bifrostContext(ctx *fasthttp.RequestCtx) (*context.Context, context.CancelFunc) {
	return lib.ConvertToBifrostContext(ctx, h.handlerStore.ShouldAllowDirectKeys(), h.config.GetHeaderFilterConfig())
}

and then reusing it across handlers. That will make future changes to context creation (additional flags, config, etc.) much easier and less error-prone.

Also applies to: 573-574, 649-650, 731-732, 803-804, 879-880, 1010-1011, 1096-1097, 1395-1396, 1447-1449, 1487-1488, 1526-1527, 1565-1566, 1647-1648, 1706-1707, 1745-1746, 1784-1785, 1823-1824

transports/bifrost-http/handlers/config.go (1)

648-701: Consider deduplicating and filtering empty entries server-side.

While the frontend filters empty strings before submission, the backend should also validate to ensure data integrity. Additionally, consider deduplicating entries in the allowlist/denylist to prevent user confusion.

🔎 Proposed enhancement for input sanitization
 	// Normalize header names to lowercase for case-insensitive matching
+	seen := make(map[string]bool)
+	var cleanedAllowlist []string
 	for i, header := range payload.Allowlist {
-		payload.Allowlist[i] = strings.ToLower(strings.TrimSpace(header))
+		normalized := strings.ToLower(strings.TrimSpace(header))
+		if normalized != "" && !seen[normalized] {
+			cleanedAllowlist = append(cleanedAllowlist, normalized)
+			seen[normalized] = true
+		}
 	}
+	payload.Allowlist = cleanedAllowlist
+
+	seen = make(map[string]bool)
+	var cleanedDenylist []string
 	for i, header := range payload.Denylist {
-		payload.Denylist[i] = strings.ToLower(strings.TrimSpace(header))
+		normalized := strings.ToLower(strings.TrimSpace(header))
+		if normalized != "" && !seen[normalized] {
+			cleanedDenylist = append(cleanedDenylist, normalized)
+			seen[normalized] = true
+		}
 	}
+	payload.Denylist = cleanedDenylist
ui/app/workspace/config/views/headerFilterView.tsx (1)

30-47: Consider improving type safety for useFieldArray.

The as never type casts suggest a type mismatch between the form schema and useFieldArray expectations. This is a common pattern when using useFieldArray with primitive arrays (string[]) rather than object arrays.

For better type safety, you could wrap the header strings in objects:

// In schema:
allowlist: z.array(z.object({ value: z.string() })).optional()

// In useFieldArray:
name: "allowlist" // No cast needed

However, since this works correctly at runtime and the current approach is common with react-hook-form, this is a minor improvement that can be deferred.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6445a81 and ba75398.

📒 Files selected for processing (20)
  • framework/configstore/clientconfig.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
  • ui/app/workspace/config/header-filter/page.tsx
  • ui/app/workspace/config/views/headerFilterView.tsx
  • ui/components/sidebar.tsx
  • ui/lib/store/apis/baseApi.ts
  • ui/lib/store/apis/configApi.ts
  • ui/lib/types/config.ts
  • ui/lib/types/schemas.ts
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • ui/app/workspace/config/header-filter/page.tsx
  • transports/config.schema.json
  • ui/lib/types/config.ts
  • ui/lib/types/schemas.ts
  • transports/bifrost-http/server/server.go
  • ui/components/sidebar.tsx
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/lib/ctx.go
  • framework/configstore/clientconfig.go
  • framework/configstore/store.go
  • ui/app/workspace/config/views/headerFilterView.tsx
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/integrations/router.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/mcp.go
  • framework/configstore/rdb.go
  • ui/lib/store/apis/configApi.ts
  • ui/lib/store/apis/baseApi.ts
🧠 Learnings (2)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/lib/ctx.go
  • framework/configstore/clientconfig.go
  • framework/configstore/store.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/integrations/router.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/mcp.go
  • framework/configstore/rdb.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/mcp.go
🧬 Code graph analysis (13)
ui/app/workspace/config/header-filter/page.tsx (1)
ui/app/workspace/config/views/headerFilterView.tsx (1)
  • HeaderFilterView (18-257)
ui/lib/types/config.ts (1)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
transports/bifrost-http/server/server.go (3)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/clientconfig.go (1)
  • ClientConfig (36-51)
transports/bifrost-http/handlers/config.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/integrations/bedrock_test.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/lib/ctx.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/clientconfig.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/store.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/handlers/inference.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-369)
transports/bifrost-http/integrations/router.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-369)
framework/configstore/tables/config.go (4)
core/providers/gemini/types.go (1)
  • Type (783-783)
ui/lib/types/config.ts (1)
  • GlobalProxyType (269-269)
core/network/http.go (1)
  • GlobalProxyType (46-46)
core/schemas/provider.go (1)
  • NoProxy (147-147)
transports/bifrost-http/handlers/mcp.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-369)
ui/lib/store/apis/configApi.ts (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
🔇 Additional comments (27)
ui/lib/store/apis/baseApi.ts (1)

138-150: HeaderFilterConfig RTK Query tag wiring looks correct

Adding "HeaderFilterConfig" to tagTypes is consistent with the new config endpoints and should allow proper cache invalidation with no behavioral regressions.

ui/components/sidebar.tsx (1)

3-34: New “Header Filter” config nav entry is consistent and well-scoped

Importing Filter and adding the “Header Filter” sub-item under Config, gated by hasSettingsAccess and routed to /workspace/config/header-filter, aligns with existing sidebar patterns and RBAC usage.

Also applies to: 532-538

transports/bifrost-http/lib/config.go (1)

385-388: HeaderFilterConfig load/merge and persistence pipeline is coherent

The way you:

  • merge HeaderFilterConfig with DB-precedence in mergeClientConfig,
  • sync any file-/client-derived HeaderFilterConfig into the store in loadClientConfigFromFile, and
  • then hydrate ClientConfig.HeaderFilterConfig from the store via loadDefaultHeaderFilterConfig (for both file-backed and default paths)

keeps a single source of truth in the config store while still supporting initial config.json defaults and the “no store” mode. This matches how other config sections are handled.

Also applies to: 501-507, 548-559, 1578-1580, 1763-1773

transports/bifrost-http/integrations/bedrock_test.go (1)

9-10: mockHandlerStore correctly updated for new HandlerStore contract

Extending mockHandlerStore with a headerFilterConfig field and GetHeaderFilterConfig implementation, plus the added tables import, cleanly aligns the tests with the new lib.HandlerStore interface without changing existing test behavior (nil config → default filtering).

Also applies to: 17-19, 25-27

transports/bifrost-http/integrations/router.go (1)

392-399: Router now passes headerFilterConfig into ConvertToBifrostContext as intended

Using g.handlerStore.GetHeaderFilterConfig() alongside ShouldAllowDirectKeys() when calling ConvertToBifrostContext cleanly threads the global header filtering rules into every integration route without touching downstream handler logic.

ui/app/workspace/config/header-filter/page.tsx (1)

1-11: HeaderFilterPage wiring is minimal and consistent with other config pages

Client-side HeaderFilterPage simply wraps HeaderFilterView in the usual max-w-7xl layout and lives at the path referenced by the new sidebar item, which is exactly what’s needed here.

framework/configstore/clientconfig.go (1)

37-51: ClientConfig field and hash integration for HeaderFilterConfig look consistent.

The new HeaderFilterConfig field and its inclusion in GenerateClientConfigHash are wired correctly: allowlist/denylist are sorted before hashing for determinism, and nil vs empty lists intentionally have no hash impact, matching their identical runtime behavior.

No changes needed here.

Also applies to: 144-170

transports/bifrost-http/handlers/mcp.go (1)

53-73: MCP tool handler correctly adopts header filter–aware context.

Using ConvertToBifrostContext(ctx, false, h.store.GetHeaderFilterConfig()) keeps MCP requests opt-out of direct keys while still applying the global header filter configuration. This is consistent with the rest of the stack and doesn’t introduce new behavior quirks.

Looks good as-is.

framework/configstore/store.go (1)

122-129: ConfigStore interface extension for header filter config is consistent; ensure all impls are updated.

Adding GetHeaderFilterConfig / UpdateHeaderFilterConfig here fits the existing pattern for global config (proxy, restart, auth, etc.) and matches the tables.GlobalHeaderFilterConfig type.

Just make sure every concrete ConfigStore implementation and any mocks (e.g., sqlite/postgres stores, in-memory test doubles) implement these methods so the stack compiles and tests continue to pass.

ui/lib/types/config.ts (1)

302-313: TS config types for global header filter match backend and defaults correctly.

GlobalHeaderFilterConfig and DefaultGlobalHeaderFilterConfig align with tables.GlobalHeaderFilterConfig, and wiring it into BifrostConfig via header_filter_config?: GlobalHeaderFilterConfig maintains backward compatibility (field is optional) while exposing the new capability to the UI.

No changes needed here.

Also applies to: 321-329

framework/configstore/rdb.go (1)

1952-1981: LGTM! Methods follow established patterns.

The GetHeaderFilterConfig and UpdateHeaderFilterConfig methods are consistent with existing governance config methods like GetProxyConfig and GetRestartRequiredConfig. The error handling, nil checks, and JSON marshalling patterns align well with the codebase conventions.

framework/configstore/tables/config.go (2)

40-49: LGTM! Well-documented struct with clear filter logic.

The GlobalHeaderFilterConfig struct is well-designed with clear documentation explaining the filter precedence logic. The JSON tags with omitempty allow for flexible partial configuration.


12-12: LGTM!

The constant follows the established naming convention for governance config keys.

transports/bifrost-http/server/server.go (2)

841-856: LGTM! Clean hot-reload implementation.

The method correctly stores the header filter config in memory and provides useful debug logging. Unlike proxy configuration, header filter changes take effect immediately without requiring a restart, which is appropriate since the filter is applied per-request.


66-66: LGTM!

The interface extension is consistent with the existing callback pattern.

ui/lib/store/apis/configApi.ts (2)

90-106: LGTM! RTK Query endpoints follow established patterns.

The new endpoints correctly mirror the existing proxy config pattern. The cache invalidation strategy invalidating both HeaderFilterConfig and Config tags ensures the UI stays synchronized when header filter settings change.


124-125: The hook name useUpdateHeaderFilterConfigMutation is correct and consistent throughout the codebase. No typo exists.

Likely an incorrect or invalid review comment.

transports/bifrost-http/handlers/config.go (2)

626-645: LGTM! Handler follows established patterns.

The getHeaderFilterConfig handler correctly returns empty arrays when config is nil, providing a consistent response structure for the frontend.


163-169: LGTM!

Header filter config is correctly included in the aggregated config response, consistent with how proxy config is handled.

ui/app/workspace/config/views/headerFilterView.tsx (4)

49-59: LGTM! Proper form synchronization with fetched data.

The useEffect correctly resets the form when server data is fetched, ensuring arrays are properly initialized even if the server returns undefined values.


61-73: LGTM! Clean data sanitization before submission.

The onSubmit handler correctly filters out empty strings before sending to the API, preventing invalid entries from being persisted.


114-126: LGTM! Clear and informative filter precedence documentation.

The alert clearly explains the filter logic to users, which matches the implementation in the backend. This is excellent UX design.


239-251: LGTM! Security transparency.

Displaying the list of always-blocked headers provides good transparency to users about the security measures in place.

transports/bifrost-http/lib/ctx.go (4)

100-116: LGTM! Security denylist is comprehensive.

The security denylist correctly blocks sensitive headers that could be exploited:

  • Authentication headers (authorization, proxy-authorization, cookie)
  • Connection headers (host, content-length, connection, transfer-encoding)
  • API key headers that could override governance controls

118-145: LGTM! Filter logic implementation is correct.

The shouldAllowHeader function correctly implements the documented precedence:

  1. Nil config → allow all
  2. Allowlist check (if present) → must be in list
  3. Denylist check (if present) → must not be in list
  4. Both empty → allow all

The implementation matches the documentation in GlobalHeaderFilterConfig and the UI explanation.


287-294: LGTM! Filter application is in the correct order.

The security denylist is checked first (always enforced), followed by the configurable filter. This ensures security-sensitive headers cannot be forwarded even if mistakenly added to an allowlist.


80-80: All callers have been properly updated. All 21 call sites across router.go, mcp.go, and inference.go pass the required three arguments: context, allowDirectKeys flag, and headerFilterConfig.

@akshaydeo akshaydeo force-pushed the 12-26-adds_allowlist_and_denylist_for_headers branch from ba75398 to a65444e Compare December 26, 2025 12:08
Copy link
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

🧹 Nitpick comments (4)
ui/app/workspace/config/views/clientSettingsView.tsx (3)

30-45: Consider order-insensitive comparison for header filter config.

The current comparison is order-sensitive, meaning ["a", "b"] and ["b", "a"] are considered different. While this works for tracking local edits, it could cause false "unsaved changes" detection if the server returns headers in a different order than stored locally.

🔎 Proposed order-insensitive comparison
 function headerFilterConfigEqual (a?: GlobalHeaderFilterConfig, b?: GlobalHeaderFilterConfig): boolean {
   const aAllowlist = a?.allowlist || []
   const bAllowlist = b?.allowlist || []
   const aDenylist = a?.denylist || []
   const bDenylist = b?.denylist || []

   if (aAllowlist.length !== bAllowlist.length || aDenylist.length !== bDenylist.length) {
     return false
   }

+  const sortedAAllow = [...aAllowlist].sort()
+  const sortedBAllow = [...bAllowlist].sort()
+  const sortedADeny = [...aDenylist].sort()
+  const sortedBDeny = [...bDenylist].sort()
+
   return (
-    aAllowlist.every((v, i) => v === bAllowlist[i]) &&
-    aDenylist.every((v, i) => v === bDenylist[i])
+    sortedAAllow.every((v, i) => v === sortedBAllow[i]) &&
+    sortedADeny.every((v, i) => v === sortedBDeny[i])
   )
 }

84-99: Consider adding a defensive check before accessing bifrostConfig.

The non-null assertion bifrostConfig! on line 94 is technically safe due to the button's disabled logic, but an explicit guard makes the code more robust against future refactoring.

🔎 Proposed defensive check
   const handleSave = useCallback(async () => {
+    if (!bifrostConfig) return
     try {
       // Clean up empty strings from header filter config
       const cleanedConfig = {
         ...localConfig,
         header_filter_config: {
           allowlist: (localConfig.header_filter_config?.allowlist || []).filter((h) => h && h.trim().length > 0),
           denylist: (localConfig.header_filter_config?.denylist || []).filter((h) => h && h.trim().length > 0),
         },
       }
-      await updateCoreConfig({ ...bifrostConfig!, client_config: cleanedConfig }).unwrap()
+      await updateCoreConfig({ ...bifrostConfig, client_config: cleanedConfig }).unwrap()
       toast.success("Client settings updated successfully.")
     } catch (error) {
       toast.error(getErrorMessage(error))
     }
   }, [bifrostConfig, localConfig, updateCoreConfig])

258-278: Using index as key for dynamic lists may cause subtle issues.

When items are removed from the middle of the list, React may preserve DOM state incorrectly because keys shift. For controlled inputs this usually works, but could cause issues with focus or animations. Consider using a stable identifier if problems arise.

One approach would be to generate unique IDs when adding headers:

// Instead of storing string[], store { id: string, value: string }[]
// Then use key={item.id} in the map

For now, since these are simple controlled inputs and the list is typically small, this is acceptable.

Also applies to: 302-322

transports/bifrost-http/handlers/config.go (1)

25-35: Header filter config wiring is correct; consider order-insensitive equality

The new pieces here hang together well:

  • ConfigManager gains ReloadHeaderFilterConfig, and updateConfig invokes it only when the client’s HeaderFilterConfig actually differs.
  • getConfig exposes header_filter_config from the store alongside existing config sections, which is what the UI and API need.

One behavioral nuance: headerFilterConfigEqual uses slices.Equal directly, so allowlist/denylist comparisons are order‑sensitive and treat nil vs non‑nil GlobalHeaderFilterConfig as different even when both lists are empty. Since the filter semantics are set‑like (order doesn’t matter, and “no headers configured” vs “empty lists” behave the same in ConvertToBifrostContext), you might want to normalize before comparison (e.g., sort and/or treat empty configs as equivalent) to avoid unnecessary reloads when only list ordering changes.

If you’re fine with “any structural difference triggers a reload,” the current implementation is otherwise sound.

Also applies to: 161-167, 176-176, 188-192, 223-225, 276-285, 633-642

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba75398 and a65444e.

📒 Files selected for processing (16)
  • framework/configstore/clientconfig.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
  • ui/app/workspace/config/views/clientSettingsView.tsx
  • ui/lib/types/config.ts
  • ui/lib/types/schemas.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • framework/configstore/tables/config.go
  • framework/configstore/rdb.go
  • transports/bifrost-http/lib/config.go
  • ui/lib/types/config.ts
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/config.schema.json
  • ui/lib/types/schemas.ts
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • framework/configstore/clientconfig.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/integrations/router.go
  • ui/app/workspace/config/views/clientSettingsView.tsx
  • framework/configstore/store.go
  • transports/bifrost-http/server/server.go
🧠 Learnings (2)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • framework/configstore/clientconfig.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/integrations/router.go
  • framework/configstore/store.go
  • transports/bifrost-http/server/server.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/server/server.go
🧬 Code graph analysis (7)
transports/bifrost-http/handlers/mcp.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-369)
transports/bifrost-http/handlers/config.go (3)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/clientconfig.go (1)
  • ClientConfig (36-51)
transports/bifrost-http/lib/ctx.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/handlers/inference.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-369)
transports/bifrost-http/integrations/router.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-369)
ui/app/workspace/config/views/clientSettingsView.tsx (4)
ui/lib/types/config.ts (3)
  • CoreConfig (336-350)
  • DefaultGlobalHeaderFilterConfig (310-313)
  • GlobalHeaderFilterConfig (304-307)
ui/app/_fallbacks/enterprise/lib/contexts/rbacContext.tsx (1)
  • useRbac (63-65)
ui/lib/store/apis/baseApi.ts (1)
  • getErrorMessage (174-199)
ui/components/ui/input.tsx (1)
  • Input (15-69)
framework/configstore/store.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
🔇 Additional comments (14)
ui/app/workspace/config/views/clientSettingsView.tsx (3)

101-164: LGTM!

The header filter list handlers are well-structured with proper memoization via useCallback. The lowercase normalization on input change aligns with the security requirements mentioned in the PR objectives.


166-221: LGTM!

The component layout and toggle sections are well-structured. RBAC checks are consistently applied to disable controls when the user lacks update permissions. The conditional display of dropped request count is a nice UX touch.


223-349: LGTM!

The header forwarding section provides excellent documentation through the Alert components, clearly explaining the allowlist/denylist behavior and the always-blocked security headers. The UI flows for managing headers are intuitive.

framework/configstore/clientconfig.go (2)

37-50: ClientConfig: header filter field wiring looks consistent

Adding HeaderFilterConfig *tables.GlobalHeaderFilterConfig with omitempty and a clear comment fits the existing config surface and keeps it optional without breaking JSON shape.


144-170: HeaderFilterConfig hashing is deterministic and aligned with semantics

Including sorted allowlist/denylist in GenerateClientConfigHash (with explicit prefixes) makes hash changes stable and order‑insensitive, matching how other slice fields are handled and what the filter logic expects.

transports/bifrost-http/handlers/inference.go (1)

450-451: Consistent propagation of header filter config into all inference/file handlers

Passing h.config.GetHeaderFilterConfig() into ConvertToBifrostContext for every handler keeps header forwarding rules centralized and guarantees that the allowlist/denylist is respected across all Bifrost HTTP endpoints without altering existing cancellation/error behavior.

Also applies to: 573-574, 649-650, 731-732, 803-805, 879-880, 1010-1011, 1096-1097, 1395-1397, 1448-1449, 1487-1488, 1526-1527, 1565-1566, 1647-1648, 1706-1707, 1745-1746, 1784-1785, 1823-1824

transports/bifrost-http/integrations/router.go (1)

393-400: Integration router now correctly applies global header filter config

Using g.handlerStore.GetHeaderFilterConfig() when building bifrostCtx aligns integrations with the core handlers so that x‑bf‑eh‑* forwarding is governed by the same allowlist/denylist rules on all routes.

transports/bifrost-http/handlers/mcp.go (1)

67-68: MCP execution correctly opts into header filtering

Passing h.store.GetHeaderFilterConfig() into ConvertToBifrostContext for MCP tool execution keeps header behavior consistent with the rest of the transport while still disallowing direct keys for this endpoint.

framework/configstore/store.go (1)

126-129: ConfigStore extension for header filter config is well-scoped

Adding dedicated GetHeaderFilterConfig / UpdateHeaderFilterConfig methods mirrors existing global config CRUD patterns (e.g., proxy, restart_required) and gives a clear persistence API for GlobalHeaderFilterConfig.

transports/bifrost-http/server/server.go (1)

53-67: Server-side reload hook for header filter config is straightforward and sufficient

Extending ServerCallbacks and implementing ReloadHeaderFilterConfig by updating s.Config.ClientConfig.HeaderFilterConfig and logging allowlist/denylist sizes gives a clear, low‑friction way to hot‑apply header filter changes without restart and fits the existing Reload*Config patterns in this server.

Also applies to: 841-856

transports/bifrost-http/lib/ctx.go (4)

11-11: LGTM!

Imports are appropriate for the new header filtering functionality.

Also applies to: 18-18


80-80: LGTM!

The function signature extension is clean. Using a pointer allows nil to represent "no filtering config" with graceful handling inside the function.


100-116: LGTM!

Good rename to securityDenylist to clearly distinguish the always-enforced security list from the user-configurable denylist. The coverage of security-sensitive headers (auth, proxy-auth, cookies, connection headers, and key-override headers) is comprehensive.


287-294: LGTM!

Correct enforcement order: security denylist is checked first (always enforced), then the configurable filter is applied. This ensures security-sensitive headers are never forwarded regardless of user configuration.

Comment on lines +125 to +144
shouldAllowHeader := func(headerName string) bool {
if headerFilterConfig == nil {
return true
}
hasAllowlist := len(headerFilterConfig.Allowlist) > 0
hasDenylist := len(headerFilterConfig.Denylist) > 0

// If allowlist is non-empty, header must be in allowlist
if hasAllowlist {
if !slices.Contains(headerFilterConfig.Allowlist, headerName) {
return false
}
}
// If denylist is non-empty, header must not be in denylist
if hasDenylist {
if slices.Contains(headerFilterConfig.Denylist, headerName) {
return false
}
}
return true
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check the file structure and find the ctx.go file
find . -name "ctx.go" -path "*/bifrost-http/*"

Repository: maximhq/bifrost

Length of output: 95


🏁 Script executed:

# Read the ctx.go file around the flagged lines and line 286
cat -n transports/bifrost-http/lib/ctx.go | sed -n '115,160p'

Repository: maximhq/bifrost

Length of output: 2124


🏁 Script executed:

# Also check around line 286
cat -n transports/bifrost-http/lib/ctx.go | sed -n '275,300p'

Repository: maximhq/bifrost

Length of output: 1108


🏁 Script executed:

# Search for where headerFilterConfig is initialized/populated
rg -n "headerFilterConfig\s*=" transports/bifrost-http/ -A 2 -B 2

Repository: maximhq/bifrost

Length of output: 402


🏁 Script executed:

# Search for normalization of allowlist/denylist (ToLower, toLower, etc.)
rg -n "ToLower|toLower" transports/bifrost-http/lib/ctx.go

Repository: maximhq/bifrost

Length of output: 980


🏁 Script executed:

# Search for GlobalHeaderFilterConfig or config structure definition
rg -n "GlobalHeaderFilterConfig|Allowlist|Denylist" transports/bifrost-http/ --type=go -B 2 -A 2

Repository: maximhq/bifrost

Length of output: 8019


🏁 Script executed:

# Check git status to understand if this is part of a PR stack
git log --oneline -10 2>/dev/null || echo "Git not available"

Repository: maximhq/bifrost

Length of output: 106


Case-sensitivity mismatch in header filter config comparison.

The incoming headerName is lowercased at line 286 before being passed to shouldAllowHeader(), but slices.Contains() at lines 134 and 140 performs case-sensitive comparison against headerFilterConfig.Allowlist and headerFilterConfig.Denylist. The config values are not normalized to lowercase anywhere (config.go lines 553-557, server.go line 842). This means headers configured as "Content-Type" in the allowlist will not match the lowercased incoming header "content-type", causing the filter to reject valid headers.

Use case-insensitive comparison when checking allowlist/denylist:

 		if hasAllowlist {
-			if !slices.Contains(headerFilterConfig.Allowlist, headerName) {
+			if !slices.ContainsFunc(headerFilterConfig.Allowlist, func(s string) bool {
+				return strings.EqualFold(s, headerName)
+			}) {
 				return false
 			}
 		}
 		if hasDenylist {
-			if slices.Contains(headerFilterConfig.Denylist, headerName) {
+			if slices.ContainsFunc(headerFilterConfig.Denylist, func(s string) bool {
+				return strings.EqualFold(s, headerName)
+			}) {
 				return false
 			}
 		}
📝 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
shouldAllowHeader := func(headerName string) bool {
if headerFilterConfig == nil {
return true
}
hasAllowlist := len(headerFilterConfig.Allowlist) > 0
hasDenylist := len(headerFilterConfig.Denylist) > 0
// If allowlist is non-empty, header must be in allowlist
if hasAllowlist {
if !slices.Contains(headerFilterConfig.Allowlist, headerName) {
return false
}
}
// If denylist is non-empty, header must not be in denylist
if hasDenylist {
if slices.Contains(headerFilterConfig.Denylist, headerName) {
return false
}
}
return true
}
shouldAllowHeader := func(headerName string) bool {
if headerFilterConfig == nil {
return true
}
hasAllowlist := len(headerFilterConfig.Allowlist) > 0
hasDenylist := len(headerFilterConfig.Denylist) > 0
// If allowlist is non-empty, header must be in allowlist
if hasAllowlist {
if !slices.ContainsFunc(headerFilterConfig.Allowlist, func(s string) bool {
return strings.EqualFold(s, headerName)
}) {
return false
}
}
// If denylist is non-empty, header must not be in denylist
if hasDenylist {
if slices.ContainsFunc(headerFilterConfig.Denylist, func(s string) bool {
return strings.EqualFold(s, headerName)
}) {
return false
}
}
return true
}
🤖 Prompt for AI Agents
In transports/bifrost-http/lib/ctx.go around lines 125 to 145, the header
allow/deny checks are case-sensitive while incoming headerName is lowercased
elsewhere; update the comparison to be case-insensitive by normalizing values or
using case-insensitive comparison. Fix by either (A) normalizing
headerFilterConfig.Allowlist and headerFilterConfig.Denylist to lowercase when
the config is loaded so slices.Contains can be used against the
already-lowercased headerName, or (B) change the checks here to compare using
strings.EqualFold (or compare against a precomputed lowercase set) when testing
membership in allowlist/denylist; ensure both allowlist and denylist comparisons
use the same case-insensitive approach so headers match regardless of case.

@akshaydeo akshaydeo force-pushed the 12-26-adds_allowlist_and_denylist_for_headers branch from a65444e to 312c22a Compare December 26, 2025 13:18
Copy link
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: 0

♻️ Duplicate comments (1)
transports/bifrost-http/lib/ctx.go (1)

118-145: Header filter allow/deny checks are case-sensitive despite lowercasing header names.

labelName is normalized to lowercase before calling shouldAllowHeader, but the allow/deny checks use slices.Contains against the raw Allowlist/Denylist, which are not normalized. A config entry like "Content-Type" will not match the incoming "content-type", so filters behave unexpectedly and contradict the stated case‑insensitive semantics.

Recommend making membership checks case‑insensitive (or normalizing config to lowercase on load). One local fix is:

Proposed fix: use case-insensitive membership via strings.EqualFold
-		hasAllowlist := len(headerFilterConfig.Allowlist) > 0
-		hasDenylist := len(headerFilterConfig.Denylist) > 0
+		allowlist := headerFilterConfig.Allowlist
+		denylist := headerFilterConfig.Denylist
+		hasAllowlist := len(allowlist) > 0
+		hasDenylist := len(denylist) > 0
@@
-		if hasAllowlist {
-			if !slices.Contains(headerFilterConfig.Allowlist, headerName) {
+		if hasAllowlist {
+			if !slices.ContainsFunc(allowlist, func(s string) bool {
+				return strings.EqualFold(s, headerName)
+			}) {
 				return false
 			}
 		}
 		// If denylist is non-empty, header must not be in denylist
-		if hasDenylist {
-			if slices.Contains(headerFilterConfig.Denylist, headerName) {
+		if hasDenylist {
+			if slices.ContainsFunc(denylist, func(s string) bool {
+				return strings.EqualFold(s, headerName)
+			}) {
 				return false
 			}
 		}

This keeps header matching truly case-insensitive while leaving config storage format unchanged.

🧹 Nitpick comments (5)
ui/lib/types/schemas.ts (1)

680-691: Global header filter schemas/types line up with backend; optional normalization.

The Zod schemas and inferred types correctly mirror the backend GlobalHeaderFilterConfig (allowlist/denylist) and the header_filter_config wrapper shape, so they should interop cleanly with /api/config. If header names are meant to be case-insensitive, you may optionally add a .transform() (e.g., s.trim().toLowerCase()) on the string items here so UI input is canonicalized before it hits the backend filters.

Also applies to: 709-710

transports/bifrost-http/handlers/config.go (1)

34-35: Header filter config wiring is sound; verify persistence and equality semantics.

  • Extending ConfigManager with ReloadHeaderFilterConfig and calling it from updateConfig when HeaderFilterConfig changes is a good way to hot‑reload filter rules without a restart.
  • getConfig exposing header_filter_config via ConfigStore.GetHeaderFilterConfig gives the UI a clean, focused payload in addition to whatever is on client_config.
  • headerFilterConfigEqual’s pointer/len-based comparison (treating nil vs empty slices as equal and being order-sensitive) is reasonable; just be aware that reordering entries alone will trigger a reload.

The only thing that’s not obvious from this file is whether HeaderFilterConfig changes are persisted to the same storage that backs GetHeaderFilterConfig (e.g., via UpdateHeaderFilterConfig or inside UpdateClientConfig). If that linkage isn’t already in place in configstore, consider adding it so that:

  • GET /api/config always returns the latest header filter config, and
  • the configuration survives process restarts.

Also applies to: 161-167, 276-285, 633-642

transports/bifrost-http/lib/config.go (1)

36-41: Header filter config bootstrapping and HandlerStore exposure look correct.

  • Extending HandlerStore with GetHeaderFilterConfig and implementing it on Config as a lock-free accessor over ClientConfig.HeaderFilterConfig is consistent with the existing ShouldAllowDirectKeys pattern.
  • Both config load paths (file-backed and default/SQLite) now call loadDefaultHeaderFilterConfig, so runtime always picks up whatever ConfigStore.GetHeaderFilterConfig returns.
  • loadClientConfigFromFile syncing HeaderFilterConfig into the store and mergeClientConfig’s “DB wins, fill missing allow/deny lists from file” behavior give you a sane single source of truth while still honoring file defaults.

If you later decide to canonicalize header names (e.g., trimming and lowercasing allowlist/denylist entries once up front), loadDefaultHeaderFilterConfig and the sync block in loadClientConfigFromFile are good central places to do that so all consumers see normalized data.

Also applies to: 355-373, 501-506, 548-559, 1578-1580, 1763-1773, 2065-2071

ui/app/workspace/config/views/clientSettingsView.tsx (2)

280-300: Using array index as key can cause input focus and value issues when items are removed.

When items are added or removed from the middle of the list, React's reconciliation may not correctly associate inputs with their values, potentially causing the cursor to jump or values to appear in wrong inputs.

Consider generating a stable unique ID when adding items, or using a combination approach.

🔎 Proposed approach using stable IDs

One approach is to store objects with IDs instead of plain strings:

// In state, use objects with stable IDs:
interface HeaderEntry { id: string; value: string; }

// When adding:
const handleAddAllowlistHeader = useCallback(() => {
  setLocalConfig((prev) => ({
    ...prev,
    header_filter_config: {
      ...prev.header_filter_config,
      allowlist: [...(prev.header_filter_config?.allowlist || []), { id: crypto.randomUUID(), value: "" }],
    },
  }));
}, []);

Alternatively, if keeping plain strings, you could use a composite key, though this is less robust:

key={`${header}-${index}`}

316-336: Same key={index} concern applies to denylist.

As noted for the allowlist above, using index as key here has the same potential for input confusion when items are removed. The same mitigation approach would apply.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a65444e and 312c22a.

📒 Files selected for processing (16)
  • framework/configstore/clientconfig.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
  • ui/app/workspace/config/views/clientSettingsView.tsx
  • ui/lib/types/config.ts
  • ui/lib/types/schemas.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/config.schema.json
  • framework/configstore/store.go
  • framework/configstore/clientconfig.go
  • framework/configstore/tables/config.go
  • ui/lib/types/config.ts
  • transports/bifrost-http/handlers/inference.go
  • framework/configstore/rdb.go
  • transports/bifrost-http/integrations/router.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/server/server.go
  • ui/app/workspace/config/views/clientSettingsView.tsx
  • transports/bifrost-http/handlers/config.go
  • ui/lib/types/schemas.ts
  • transports/bifrost-http/lib/config.go
🧠 Learnings (2)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config.go
🧬 Code graph analysis (4)
transports/bifrost-http/lib/ctx.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/handlers/mcp.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-369)
ui/app/workspace/config/views/clientSettingsView.tsx (3)
ui/lib/types/config.ts (3)
  • DefaultGlobalHeaderFilterConfig (310-313)
  • GlobalHeaderFilterConfig (304-307)
  • CoreConfig (336-350)
ui/lib/store/apis/baseApi.ts (1)
  • getErrorMessage (174-199)
ui/components/ui/input.tsx (1)
  • Input (15-69)
transports/bifrost-http/handlers/config.go (4)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/clientconfig.go (1)
  • ClientConfig (36-51)
transports/bifrost-http/handlers/utils.go (1)
  • SendError (35-44)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
🔇 Additional comments (6)
transports/bifrost-http/handlers/mcp.go (1)

67-68: MCP execute path correctly participates in header filtering.

Passing h.store.GetHeaderFilterConfig() into ConvertToBifrostContext aligns MCP tool execution with the inference/router paths and ensures extra headers here respect the same filter rules.

transports/bifrost-http/server/server.go (1)

66-67: Server-side header filter reload is correct; confirm persistence path.

The new ReloadHeaderFilterConfig callback cleanly updates the in-memory ClientConfig.HeaderFilterConfig and logs a concise summary, which is enough for live header filtering to see changes.

Since this method is intentionally in-memory only, please confirm that the handler/configstore path (e.g., UpdateClientConfig and/or UpdateHeaderFilterConfig) also persists the same config so that:

  • GetHeaderFilterConfig used by GET /api/config returns the updated version, and
  • the filter configuration survives process restarts.

Also applies to: 841-856

ui/app/workspace/config/views/clientSettingsView.tsx (4)

30-42: Order-sensitive comparison may not detect reordered headers as "unchanged".

The headerFilterConfigEqual function compares arrays element-by-element by index. If users manually reorder headers externally or via future drag-and-drop functionality, identical sets in different orders would be flagged as changed.

For the current UI (which doesn't support reordering), this works correctly. If reordering is added later, consider using a Set-based comparison.


81-96: Good sanitization of empty entries before save.

The cleanup logic correctly filters out empty and whitespace-only strings from both allowlist and denylist before persisting. This prevents invalid entries from being saved.


223-270: Clear and helpful documentation for users.

The accordion sections provide good explanations of how the allowlist/denylist precedence works and which headers are always blocked for security. This helps users understand the filtering behavior without needing external documentation.


119-127: Lowercase normalization applied correctly on input change.

Both handleAllowlistChange and handleDenylistChange correctly normalize header names to lowercase, ensuring case-insensitive matching as specified in the PR objectives.

Also applies to: 149-157

@akshaydeo akshaydeo force-pushed the 12-26-adds_allowlist_and_denylist_for_headers branch from 312c22a to dcb3c25 Compare December 26, 2025 16:41
Copy link
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: 0

♻️ Duplicate comments (1)
transports/bifrost-http/lib/ctx.go (1)

80-85: Header filter checks are still case‑sensitive against lowercased header names.

Incoming headers are normalized to lowercase (keyStr, labelName), but both shouldAllowHeader and the direct forwarding block compare against headerFilterConfig.Allowlist/Denylist using slices.Contains, which is case‑sensitive. The UI now lowercases entries, but configs set via JSON or direct API can still use mixed‑case names (e.g., Content-Type), which won’t match the lowercased header keys and will silently bypass the filter logic.

To make matching truly case‑insensitive and robust across all writers, consider switching these membership tests to something like slices.ContainsFunc(list, func(s string) bool { return strings.EqualFold(s, headerName) }) (and the analogous check in the direct forwarding branch), or factoring out a helper that does EqualFold for both allowlist and denylist.

Also applies to: 100-115, 124-144, 279-297, 298-321

🧹 Nitpick comments (3)
transports/bifrost-http/handlers/config.go (1)

632-642: Consider: slices.Equal is order-sensitive.

The comparison uses slices.Equal which is order-sensitive. If two configs have the same headers in different order (e.g., ["a", "b"] vs ["b", "a"]), they will be considered different, triggering unnecessary reloads.

If header order is semantically irrelevant, consider sorting before comparison or using a set-based comparison.

🔎 Optional: Order-insensitive comparison
+import "sort"
+
+// slicesEqualUnordered compares two string slices ignoring order
+func slicesEqualUnordered(a, b []string) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	aCopy := make([]string, len(a))
+	bCopy := make([]string, len(b))
+	copy(aCopy, a)
+	copy(bCopy, b)
+	sort.Strings(aCopy)
+	sort.Strings(bCopy)
+	return slices.Equal(aCopy, bCopy)
+}

 func headerFilterConfigEqual(a, b *configstoreTables.GlobalHeaderFilterConfig) bool {
 	if a == nil && b == nil {
 		return true
 	}
 	if a == nil || b == nil {
 		return false
 	}
-	return slices.Equal(a.Allowlist, b.Allowlist) && slices.Equal(a.Denylist, b.Denylist)
+	return slicesEqualUnordered(a.Allowlist, b.Allowlist) && slicesEqualUnordered(a.Denylist, b.Denylist)
 }
transports/bifrost-http/lib/config.go (1)

37-41: Header filter plumbing looks coherent; consider normalizing config values once at load.

The HandlerStore extension and the end‑to‑end wiring (client config merge, store sync via UpdateHeaderFilterConfig, default‐path load, and GetHeaderFilterConfig accessor) are consistent and match the intended “store as source of truth, file as seed” behavior; semantics for nil vs empty allow/deny lists also align with the runtime logic in ctx.go.

As a small robustness improvement, you could normalize header filter entries (e.g., strings.TrimSpace + strings.ToLower) once when merging/loading into ClientConfig.HeaderFilterConfig, so non‑UI writers (config file / direct API) can safely rely on case‑insensitive matching without having to follow the UI’s lowercase convention; this would keep ctx.go simple while hardening behavior against stray whitespace/case in persisted configs.

Also applies to: 385-388, 501-506, 548-559, 1578-1580, 1763-1773, 2065-2071

ui/app/workspace/config/views/clientSettingsView.tsx (1)

27-42: UI wiring for header forwarding looks good; trim+lowercase values to avoid subtle mismatches.

The defaulting of header_filter_config, equality helper, and the allowlist/denylist UI all line up well with the backend, and forcing value.toLowerCase() ensures the lists stay consistent with lowercased header keys in ctx.go. One small robustness tweak: consider normalizing with value.trim().toLowerCase() in handleAllowlistChange/handleDenylistChange (and optionally serializing the trimmed version in handleSave) so that stray leading/trailing spaces don’t result in entries like " custom-id" that will never match any real header name.

Also applies to: 59-65, 81-91, 98-157, 295-371

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 312c22a and dcb3c25.

📒 Files selected for processing (16)
  • framework/configstore/clientconfig.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
  • ui/app/workspace/config/views/clientSettingsView.tsx
  • ui/lib/types/config.ts
  • ui/lib/types/schemas.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • transports/config.schema.json
  • framework/configstore/store.go
  • transports/bifrost-http/handlers/inference.go
  • framework/configstore/clientconfig.go
  • framework/configstore/tables/config.go
  • ui/lib/types/schemas.ts
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/handlers/config.go
  • ui/lib/types/config.ts
  • transports/bifrost-http/lib/ctx.go
  • framework/configstore/rdb.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/mcp.go
  • ui/app/workspace/config/views/clientSettingsView.tsx
  • transports/bifrost-http/integrations/router.go
🧠 Learnings (2)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/ctx.go
  • framework/configstore/rdb.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/router.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/ctx.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/router.go
🧬 Code graph analysis (10)
transports/bifrost-http/integrations/bedrock_test.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/handlers/config.go (4)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/clientconfig.go (1)
  • ClientConfig (36-51)
transports/bifrost-http/handlers/utils.go (1)
  • SendError (35-44)
ui/lib/types/config.ts (1)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
transports/bifrost-http/lib/ctx.go (2)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/rdb.go (2)
framework/configstore/tables/config.go (4)
  • GlobalHeaderFilterConfig (46-49)
  • TableGovernanceConfig (52-55)
  • TableGovernanceConfig (58-58)
  • ConfigHeaderFilterKey (12-12)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/lib/config.go (3)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
framework/configstore/clientconfig.go (1)
  • ClientConfig (36-51)
transports/bifrost-http/server/server.go (4)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
ui/lib/types/config.ts (1)
  • GlobalHeaderFilterConfig (304-307)
transports/bifrost-http/lib/config.go (1)
  • Config (186-217)
framework/configstore/clientconfig.go (1)
  • ClientConfig (36-51)
transports/bifrost-http/handlers/mcp.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-392)
ui/app/workspace/config/views/clientSettingsView.tsx (2)
ui/lib/types/config.ts (3)
  • DefaultGlobalHeaderFilterConfig (310-313)
  • GlobalHeaderFilterConfig (304-307)
  • CoreConfig (336-350)
framework/configstore/tables/config.go (1)
  • GlobalHeaderFilterConfig (46-49)
transports/bifrost-http/integrations/router.go (1)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-392)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
🔇 Additional comments (12)
transports/bifrost-http/handlers/mcp.go (1)

67-67: LGTM!

The header filter configuration is correctly passed to ConvertToBifrostContext, aligning with the updated signature and ensuring MCP tool execution respects header filtering rules.

transports/bifrost-http/integrations/bedrock_test.go (1)

15-30: LGTM!

The mock correctly implements GetHeaderFilterConfig() to satisfy the updated lib.HandlerStore interface. The compile-time interface assertion at line 30 ensures the mock stays synchronized with interface changes.

transports/bifrost-http/handlers/config.go (2)

161-167: LGTM!

Header filter config is correctly fetched from the store and included in the response when available. The nil check ensures no empty entry is added to the response map.


276-284: LGTM!

The change detection and reload flow is well-structured. The early return on reload failure ensures partial updates don't persist, maintaining configuration consistency.

ui/lib/types/config.ts (3)

302-313: LGTM!

The GlobalHeaderFilterConfig interface and default constant are correctly defined and align with the Go backend struct at framework/configstore/tables/config.go. The optional array types match the omitempty JSON tags in Go.


327-327: LGTM!

The header_filter_config field is correctly added to BifrostConfig, enabling the UI to receive and display the header filter configuration from the API response.


349-349: LGTM!

The header_filter_config field in CoreConfig matches the Go ClientConfig struct, ensuring type consistency for configuration updates.

framework/configstore/rdb.go (2)

1952-1969: LGTM!

The GetHeaderFilterConfig implementation follows the established pattern used by GetProxyConfig and other governance config retrieval methods. The nil handling for both missing records and empty values is appropriate.


1971-1981: LGTM!

The UpdateHeaderFilterConfig implementation is straightforward and consistent with UpdateProxyConfig. Using Save with the governance config key ensures proper upsert behavior.

transports/bifrost-http/server/server.go (2)

66-66: LGTM!

The ReloadHeaderFilterConfig method is correctly added to the ServerCallbacks interface, enabling the config handler to trigger runtime updates.


841-856: LGTM!

The implementation correctly stores the header filter config in ClientConfig.HeaderFilterConfig and includes defensive nil checks before accessing slice lengths. The logging provides useful visibility into the configuration state.

The pattern matches ReloadProxyConfig above, maintaining consistency across runtime configuration updates.

transports/bifrost-http/integrations/router.go (1)

383-399: Passing header filter config into ConvertToBifrostContext is consistent and minimal.

The updated call cleanly threads GetHeaderFilterConfig() into context creation without affecting the existing request/streaming pipeline, and lines up with the new HandlerStore contract in lib/config.go.

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