Skip to content

feat: enforce routing allowlist in core and propagate VK provider constraints via context key - #3936

Merged
akshaydeo merged 1 commit into
devfrom
06-01-feat_add_governance_allowlist_support_for_all_provider_routing
Jun 9, 2026
Merged

feat: enforce routing allowlist in core and propagate VK provider constraints via context key#3936
akshaydeo merged 1 commit into
devfrom
06-01-feat_add_governance_allowlist_support_for_all_provider_routing

Conversation

@Pratham-Mishra04

@Pratham-Mishra04 Pratham-Mishra04 commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Introduces a fail-closed routing allowlist enforcement mechanism so that Virtual Key provider restrictions cannot be bypassed by user-specified provider prefixes, downstream routing plugins, or any other layer that runs after governance has evaluated the request.

Changes

  • Added BifrostContextKeyRoutingAllowedProviders context key ([]ModelProvider) that plugins can set to declare which providers are valid for a given request.
  • Governance plugin now publishes the VK's ProviderConfigs providers to this context key after evaluating a Virtual Key. An empty slice means no provider is permitted (fail-closed).
  • handleRequest and handleStreamRequest in core now read this context key after all PreRequestHook plugins have run and enforce it as a hard constraint: if the resolved primary provider is not in the allowlist, the request is rejected with HTTP 400. Fallbacks targeting non-allowed providers are silently filtered out via the new filterFallbacksByAllowlist helper.
  • Model catalog resolver cooperatively intersects its catalog candidates with the allowlist before selecting a provider, producing routing-engine log entries explaining why candidates were excluded.
  • Documentation updated to describe the two-level enforcement model (cooperative filtering for observability, hard enforcement in core for guarantee) and the semantics for custom plugins using the same key.

The two-level design is intentional: cooperative filtering in the catalog resolver and enterprise LB surfaces routing decisions in observability logs, while core enforcement makes the constraint a guarantee that no plugin or user input can circumvent.

Type of change

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

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./...
  • Configure a Virtual Key with provider_configs restricted to a specific provider (e.g., openai).
  • Send a request using a provider/model prefix targeting a non-allowed provider (e.g., anthropic/claude-3). Expect HTTP 400 with a message indicating the provider is not permitted.
  • Send a request with a valid provider prefix. Expect the request to succeed.
  • Configure fallbacks that include both allowed and non-allowed providers. Verify that only allowed fallbacks are attempted.
  • Set BifrostContextKeyRoutingAllowedProviders to an empty slice from a custom plugin and confirm the request fails closed with HTTP 400.

Breaking changes

  • Yes
  • No

Requests that previously succeeded by specifying a provider prefix not covered by the Virtual Key's provider_configs will now be rejected with HTTP 400. Any deployment relying on user-supplied provider prefixes overriding VK restrictions must update those requests to use a permitted provider.

Security considerations

This change closes a governance bypass where a user or downstream plugin could route a request to a provider not permitted by the Virtual Key's configuration. The enforcement is fail-closed: an empty allowlist denies all providers rather than allowing all. Custom plugins that set BifrostContextKeyRoutingAllowedProviders should be aware that an empty slice will block the request entirely.

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

Summary by CodeRabbit

  • New Features

    • Enforced routing allowlists: requests targeting non-permitted providers are rejected with HTTP 400.
    • Provider selection and fallback lists are automatically pruned to permitted providers so routing only considers allowed options.
    • Governance-provided allowlists are published early in request processing and respected by downstream routing/resolver logic.
  • Documentation

    • Added guide explaining allowlist enforcement, observability notes, and fail-closed behavior.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR threads a governance-provided provider allowlist through request context, has the model catalog resolver intersect catalog candidates with that allowlist, and enforces the allowlist in core handlers by rejecting disallowed primaries and filtering/persisting allowed fallbacks (fail-closed when empty).

Changes

Routing Governance Allowlist Enforcement

Layer / File(s) Summary
Context key and governance publishing
core/schemas/bifrost.go, plugins/governance/main.go
Adds BifrostContextKeyRoutingAllowedProviders and publishes the virtual key's configured provider allowlist into the Bifrost context in PreRequestHook when a virtual key with provider configs exists.
Model catalog resolver allowlist filtering
plugins/modelcatalogresolver/main.go
ResolveProviderFromCatalog reads the allowlist from context, intersects catalog candidates with it, logs excluded/remaining candidates, returns ("", nil) when all candidates are pruned, otherwise continues selection from the filtered (sorted) candidate list.
Core handler allowlist validation and fallback filtering
core/bifrost.go, docs/providers/provider-routing.mdx
Adds filterFallbacksByAllowlist; handleRequest and handleStreamRequest reject disallowed primary providers (HTTP 400) and persist filtered fallbacks via req.SetFallbacks; docs describe two-phase enforcement and fail-closed semantics.

Sequence Diagram

sequenceDiagram
  participant Client
  participant GovernancePlugin
  participant BifrostContext
  participant ModelCatalogResolver
  participant CoreHandler
  Client->>GovernancePlugin: request (PreRequestHook)
  GovernancePlugin->>BifrostContext: publish allowed providers
  Client->>ModelCatalogResolver: ResolveProviderFromCatalog (reads allowlist)
  ModelCatalogResolver->>BifrostContext: read allowlist, filter candidates
  ModelCatalogResolver->>CoreHandler: selected provider (may be empty)
  CoreHandler->>BifrostContext: enforce allowlist on primary + filter fallbacks
Loading

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • maximhq/bifrost#3924: Both PRs add governance-derived allowlists into Bifrost context and use them to constrain fallback/provider selection.

Suggested reviewers

  • akshaydeo
  • danpiths
  • roroghost17

"I hopped through logs at break of day,
Keys tucked in pockets to show the way.
Providers counted, fallbacks trimmed neat,
Governance hummed — routing now complete.
🥕🐇"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main feature being added: enforcement of routing allowlist in core and propagation of Virtual Key provider constraints through a context key.
Description check ✅ Passed The description follows the template structure with all major sections completed: Summary, Changes, Type of change, Affected areas, How to test, Breaking changes, Security considerations, and Checklist. The content is comprehensive and detailed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-01-feat_add_governance_allowlist_support_for_all_provider_routing

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

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge; both streaming and non-streaming enforcement paths correctly reject disallowed providers with HTTP 400, the cooperative filtering in the catalog resolver is wired correctly, and fallback pruning is consistent with the hard enforcement.

The core enforcement logic is correct, the StatusCode is set on both paths via the shared helper, and allowlist semantics (fail-closed on empty, no-op when key absent) are implemented correctly. The two findings are limited to an informational data-exposure nit in the error message and a comment accuracy issue in the governance plugin; neither affects runtime behaviour.

The error message construction in core/bifrost.go (enforceRoutingAllowlist) warrants a second look for whether the full allowed-providers list should be included in the client-visible response.

Important Files Changed

Filename Overview
core/bifrost.go Adds filterFallbacksByAllowlist and enforceRoutingAllowlist helpers; wires them into both handleRequest and handleStreamRequest after pre-hooks complete. Logic is correct and the shared helper sets HTTP 400 on both paths. Error message leaks the full allowed-providers list to the caller.
core/schemas/bifrost.go Adds BifrostContextKeyRoutingAllowedProviders constant with a clear doc comment; placed in the correct const block alongside related keys.
plugins/governance/main.go Publishes VK ProviderConfigs as the routing allowlist context key early in PreRequestHook; guarded by virtualKey != nil && len(ProviderConfigs) > 0 so unrestricted VKs do not accidentally fail-close. Inline comment incorrectly describes the fail-closed mechanism as "empty-provider validation" rather than enforceRoutingAllowlist.
plugins/modelcatalogresolver/main.go Cooperative allowlist filtering added to ResolveProviderFromCatalog: intersects catalog candidates with the context key before provider selection; emits routing-engine logs for both partial-prune and all-pruned cases; moves selected := providers[0] after filtering. Logic and observability are correct.
docs/providers/provider-routing.mdx Adds a clear two-level enforcement section explaining cooperative filtering vs hard core enforcement; documents fail-closed semantics for empty allowlist and custom plugin usage.

Reviews (14): Last reviewed commit: "feat: add governance allowlist support f..." | Re-trigger Greptile

Comment thread core/bifrost.go Outdated
Comment thread plugins/modelcatalogresolver/main.go Outdated
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from cf73a90 to 565ee32 Compare May 31, 2026 21:51
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 63d908a to 50e8ded Compare May 31, 2026 21:51
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from 565ee32 to 349d846 Compare June 1, 2026 09:53
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 50e8ded to 7ad668e Compare June 1, 2026 09:53
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from 349d846 to 7a1013e Compare June 1, 2026 11:01
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 7ad668e to 53da3d4 Compare June 1, 2026 11:01
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 1, 2026
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 4edbac5 to 9f40efe Compare June 8, 2026 06:54
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from 694970e to a2384cb Compare June 8, 2026 06:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@core/bifrost.go`:
- Around line 4681-4684: The error currently builds a message including the full
allowed slice (using newBifrostErrorFromMsg and PopulateExtraFields with
variables provider and allowed) which gets serialized back to clients; change
the generated error message to a generic statement like "provider is not
permitted for this request" (do not include the contents of allowed) and set
bifrostErr.StatusCode as before, and separately log the detailed allowlist and
context (req.RequestType, model, provider, allowed) to your internal logger for
audit/debugging; apply the same change to the duplicate block that uses the same
pattern around PopulateExtraFields at the other location.
- Around line 4169-4190: RunPreRequestHooks currently runs PreRequestHook
plugins but does not enforce the routing allowlist, letting bypass callers use
providers/fallbacks set by plugins; update RunPreRequestHooks to enforce
BifrostContextKeyRoutingAllowedProviders after pipeline.RunPreRequestHooks by
validating and filtering req.Provider and req.Fallbacks (schemas.BifrostRequest)
against the allowed providers list stored on ctx, clearing or replacing any
disallowed provider entries and logging/wrapping as a schemas.BifrostError when
no allowed provider remains; use
bifrost.getPluginPipeline()/bifrost.releasePluginPipeline() unchanged but ensure
the allowlist check is done before returning so callers cannot skip enforcement.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 746cb507-3e94-4a5d-9307-e77294953639

📥 Commits

Reviewing files that changed from the base of the PR and between 694970e and a2384cb.

📒 Files selected for processing (5)
  • core/bifrost.go
  • core/schemas/bifrost.go
  • docs/providers/provider-routing.mdx
  • plugins/governance/main.go
  • plugins/modelcatalogresolver/main.go
💤 Files with no reviewable changes (2)
  • plugins/governance/main.go
  • plugins/modelcatalogresolver/main.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 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 `@core/bifrost.go`:
- Around line 4681-4684: The error currently builds a message including the full
allowed slice (using newBifrostErrorFromMsg and PopulateExtraFields with
variables provider and allowed) which gets serialized back to clients; change
the generated error message to a generic statement like "provider is not
permitted for this request" (do not include the contents of allowed) and set
bifrostErr.StatusCode as before, and separately log the detailed allowlist and
context (req.RequestType, model, provider, allowed) to your internal logger for
audit/debugging; apply the same change to the duplicate block that uses the same
pattern around PopulateExtraFields at the other location.
- Around line 4169-4190: RunPreRequestHooks currently runs PreRequestHook
plugins but does not enforce the routing allowlist, letting bypass callers use
providers/fallbacks set by plugins; update RunPreRequestHooks to enforce
BifrostContextKeyRoutingAllowedProviders after pipeline.RunPreRequestHooks by
validating and filtering req.Provider and req.Fallbacks (schemas.BifrostRequest)
against the allowed providers list stored on ctx, clearing or replacing any
disallowed provider entries and logging/wrapping as a schemas.BifrostError when
no allowed provider remains; use
bifrost.getPluginPipeline()/bifrost.releasePluginPipeline() unchanged but ensure
the allowlist check is done before returning so callers cannot skip enforcement.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 746cb507-3e94-4a5d-9307-e77294953639

📥 Commits

Reviewing files that changed from the base of the PR and between 694970e and a2384cb.

📒 Files selected for processing (5)
  • core/bifrost.go
  • core/schemas/bifrost.go
  • docs/providers/provider-routing.mdx
  • plugins/governance/main.go
  • plugins/modelcatalogresolver/main.go
💤 Files with no reviewable changes (2)
  • plugins/governance/main.go
  • plugins/modelcatalogresolver/main.go
🛑 Comments failed to post (2)
core/bifrost.go (2)

4169-4190: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

RunPreRequestHooks leaves bypass callers outside the hard allowlist check.

This helper is explicitly for entry points that do not go through handleRequest / handleStreamRequest, but it only runs PreRequestHooks and returns. Any caller that routes immediately after this method can still use a provider or fallback set on req without ever enforcing BifrostContextKeyRoutingAllowedProviders, which breaks the PR's "core hard guarantee" for those paths. Fold the same allowlist validation/filtering into this API, or make it return a validated request / *schemas.BifrostError so bypass callers cannot forget the check.

🤖 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 `@core/bifrost.go` around lines 4169 - 4190, RunPreRequestHooks currently runs
PreRequestHook plugins but does not enforce the routing allowlist, letting
bypass callers use providers/fallbacks set by plugins; update RunPreRequestHooks
to enforce BifrostContextKeyRoutingAllowedProviders after
pipeline.RunPreRequestHooks by validating and filtering req.Provider and
req.Fallbacks (schemas.BifrostRequest) against the allowed providers list stored
on ctx, clearing or replacing any disallowed provider entries and
logging/wrapping as a schemas.BifrostError when no allowed provider remains; use
bifrost.getPluginPipeline()/bifrost.releasePluginPipeline() unchanged but ensure
the allowlist check is done before returning so callers cannot skip enforcement.

4681-4684: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't echo the full routing allowlist back to clients.

The rejection message includes the entire allowed slice, and the transport layer serializes that message into the JSON error body. That turns an authorization failure into a provider-policy enumeration endpoint for the virtual key. Keep the detailed allowlist in routing logs, but return a generic 400 message to callers.

Also applies to: 4794-4797

🤖 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 `@core/bifrost.go` around lines 4681 - 4684, The error currently builds a
message including the full allowed slice (using newBifrostErrorFromMsg and
PopulateExtraFields with variables provider and allowed) which gets serialized
back to clients; change the generated error message to a generic statement like
"provider is not permitted for this request" (do not include the contents of
allowed) and set bifrostErr.StatusCode as before, and separately log the
detailed allowlist and context (req.RequestType, model, provider, allowed) to
your internal logger for audit/debugging; apply the same change to the duplicate
block that uses the same pattern around PopulateExtraFields at the other
location.

@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from a2384cb to c0c727c Compare June 8, 2026 07:18
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 9f40efe to 9b1c18b Compare June 8, 2026 07:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 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 `@core/bifrost.go`:
- Around line 4676-4688: Extract the duplicated allowlist gating logic into a
single helper (e.g., enforceRoutingAllowlist) that accepts the context,
provider, req, model and current fallbacks and returns either the filtered
fallbacks or a prepared *schemas.BifrostError; move the block that checks
ctx.Value(schemas.BifrostContextKeyRoutingAllowedProviders), constructs
newBifrostErrorFromMsg, calls PopulateExtraFields, sets StatusCode, calls
filterFallbacksByAllowlist and req.SetFallbacks into that helper and update both
the sync and streaming callers to call the helper and handle the returned error
or use the returned fallbacks.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ebfdb73f-0d04-4731-ad12-8573f5e94cb0

📥 Commits

Reviewing files that changed from the base of the PR and between a2384cb and c0c727c.

📒 Files selected for processing (5)
  • core/bifrost.go
  • core/schemas/bifrost.go
  • docs/providers/provider-routing.mdx
  • plugins/governance/main.go
  • plugins/modelcatalogresolver/main.go

@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from c0c727c to c6758e2 Compare June 8, 2026 11:55
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 0404470 to 252041c Compare June 8, 2026 12:24
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from c6758e2 to 0cb1e9f Compare June 8, 2026 12:24
@Madhuvod
Madhuvod force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from 0cb1e9f to c6758e2 Compare June 8, 2026 12:25
@Madhuvod
Madhuvod force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 252041c to 0404470 Compare June 8, 2026 12:25
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch from c6758e2 to 8be1116 Compare June 8, 2026 12:28
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-01-docs_update_docs_for_perrequesthook branch from 0404470 to 2aef19b Compare June 8, 2026 12:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@core/bifrost.go`:
- Around line 4708-4714: The routing-allowlist check (enforceRoutingAllowlist)
currently runs before RunLLMPreHooks, allowing PreLLMHook code to call
BifrostRequest.UpdateProvider(...) and change the final provider/fallbacks
without re-checking; move or duplicate the allowlist enforcement so it runs
after RunLLMPreHooks and immediately before dispatching to tryRequest /
tryStreamRequest (or add a final enforceRoutingAllowlist call inside tryRequest
and tryStreamRequest right before queue dispatch), ensuring the final provider
set from preReq.GetRequestFields() is validated against
BifrostContextKeyRoutingAllowedProviders; update all similar sites referenced
(around lines for 4817-4820, 4924-4963, 5175-5302) to perform the post-hook
allowlist enforcement.

In `@plugins/modelcatalogresolver/main.go`:
- Around line 88-95: The current logic uses len(existingFallbacks) == 0 to
decide whether to auto-populate fallbacks, which treats an explicitly empty
fallbacks slice the same as an omitted field; update the request plumbing to
carry a presence signal (either make BifrostRequest.Fallbacks a pointer/slice
pointer and treat nil as “not provided”, or add a boolean like
BifrostRequest.FallbacksProvided) and change the condition in the block that
references existingFallbacks/candidates/GetProvidersForModel to check that
presence signal (e.g. existingFallbacks == nil or !FallbacksProvided) instead of
len(existingFallbacks) == 0 so an explicit empty fallback list is respected.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5bba71b5-4d48-46bb-a5bd-1d04f1a4589c

📥 Commits

Reviewing files that changed from the base of the PR and between c0c727c and c6758e2.

📒 Files selected for processing (5)
  • core/bifrost.go
  • core/schemas/bifrost.go
  • docs/providers/provider-routing.mdx
  • plugins/governance/main.go
  • plugins/modelcatalogresolver/main.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 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 `@core/bifrost.go`:
- Around line 4708-4714: The routing-allowlist check (enforceRoutingAllowlist)
currently runs before RunLLMPreHooks, allowing PreLLMHook code to call
BifrostRequest.UpdateProvider(...) and change the final provider/fallbacks
without re-checking; move or duplicate the allowlist enforcement so it runs
after RunLLMPreHooks and immediately before dispatching to tryRequest /
tryStreamRequest (or add a final enforceRoutingAllowlist call inside tryRequest
and tryStreamRequest right before queue dispatch), ensuring the final provider
set from preReq.GetRequestFields() is validated against
BifrostContextKeyRoutingAllowedProviders; update all similar sites referenced
(around lines for 4817-4820, 4924-4963, 5175-5302) to perform the post-hook
allowlist enforcement.

In `@plugins/modelcatalogresolver/main.go`:
- Around line 88-95: The current logic uses len(existingFallbacks) == 0 to
decide whether to auto-populate fallbacks, which treats an explicitly empty
fallbacks slice the same as an omitted field; update the request plumbing to
carry a presence signal (either make BifrostRequest.Fallbacks a pointer/slice
pointer and treat nil as “not provided”, or add a boolean like
BifrostRequest.FallbacksProvided) and change the condition in the block that
references existingFallbacks/candidates/GetProvidersForModel to check that
presence signal (e.g. existingFallbacks == nil or !FallbacksProvided) instead of
len(existingFallbacks) == 0 so an explicit empty fallback list is respected.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5bba71b5-4d48-46bb-a5bd-1d04f1a4589c

📥 Commits

Reviewing files that changed from the base of the PR and between c0c727c and c6758e2.

📒 Files selected for processing (5)
  • core/bifrost.go
  • core/schemas/bifrost.go
  • docs/providers/provider-routing.mdx
  • plugins/governance/main.go
  • plugins/modelcatalogresolver/main.go
🛑 Comments failed to post (2)
core/bifrost.go (1)

4708-4714: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Re-check the allowlist after RunLLMPreHooks.

This gate runs before tryRequest/tryStreamRequest, but both paths still execute RunLLMPreHooks and then route using the provider from preReq.GetRequestFields(). A later LLM plugin can therefore call UpdateProvider(...) and move the attempt — including fallback attempts — onto a provider that was never checked against BifrostContextKeyRoutingAllowedProviders. The hard deny needs to run on the final provider/fallback set after pre-hooks, or inside tryRequest/tryStreamRequest before queue dispatch.

Based on learnings: PreLLMHook provider switching is intentionally supported via BifrostRequest.UpdateProvider(...), and as per coding guidelines the routing allowlist is the fail-closed contract for final provider selection.

Also applies to: 4817-4820, 4924-4963, 5175-5302

🤖 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 `@core/bifrost.go` around lines 4708 - 4714, The routing-allowlist check
(enforceRoutingAllowlist) currently runs before RunLLMPreHooks, allowing
PreLLMHook code to call BifrostRequest.UpdateProvider(...) and change the final
provider/fallbacks without re-checking; move or duplicate the allowlist
enforcement so it runs after RunLLMPreHooks and immediately before dispatching
to tryRequest / tryStreamRequest (or add a final enforceRoutingAllowlist call
inside tryRequest and tryStreamRequest right before queue dispatch), ensuring
the final provider set from preReq.GetRequestFields() is validated against
BifrostContextKeyRoutingAllowedProviders; update all similar sites referenced
(around lines for 4817-4820, 4924-4963, 5175-5302) to perform the post-hook
allowlist enforcement.

Sources: Coding guidelines, Learnings

plugins/modelcatalogresolver/main.go (1)

88-95: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don't overwrite an explicit fallbacks: [] with catalog fallbacks.

Line 95 only checks len(existingFallbacks) == 0, so omitted fallbacks and an explicitly empty slice are treated the same. That means a caller trying to disable failover can still get cross-provider fallbacks injected here, which changes routing behavior and may forward prompts to providers they intentionally opted out of. This needs a field-presence signal from BifrostRequest instead of a length check.

Also applies to: 95-104

🤖 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 `@plugins/modelcatalogresolver/main.go` around lines 88 - 95, The current logic
uses len(existingFallbacks) == 0 to decide whether to auto-populate fallbacks,
which treats an explicitly empty fallbacks slice the same as an omitted field;
update the request plumbing to carry a presence signal (either make
BifrostRequest.Fallbacks a pointer/slice pointer and treat nil as “not
provided”, or add a boolean like BifrostRequest.FallbacksProvided) and change
the condition in the block that references
existingFallbacks/candidates/GetProvidersForModel to check that presence signal
(e.g. existingFallbacks == nil or !FallbacksProvided) instead of
len(existingFallbacks) == 0 so an explicit empty fallback list is respected.

akshaydeo commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Merge activity

  • Jun 9, 5:17 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 9, 5:22 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo changed the base branch from 06-01-docs_update_docs_for_perrequesthook to graphite-base/3936 June 9, 2026 05:21
@akshaydeo
akshaydeo changed the base branch from graphite-base/3936 to dev June 9, 2026 05:21
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review June 9, 2026 05:21

The base branch was changed.

@akshaydeo
akshaydeo merged commit c8b2a48 into dev Jun 9, 2026
12 of 13 checks passed
@akshaydeo
akshaydeo deleted the 06-01-feat_add_governance_allowlist_support_for_all_provider_routing branch June 9, 2026 05:22
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.

3 participants