Skip to main content
v2.3.0
Pass-through routes and file objects now work differently with virtual keys. Read this changelog in full before you upgrade. Every one of these changes strengthens Bifrost’s security posture. If you have questions, reach out to us.

Changelog

v2.3.0 moves the enterprise gateway onto OSS transports v2.3.0. Teams, business units and customers can now hold an access profile that governs every member and key under them. Adaptive routing now holds provider keys that a provider refuses and rechecks them, and it records a routing history with an Incidents page. Alerting adds business unit and user scopes, plus rules that watch every entity of a scope and send one digest per channel. Identity sync adds reconcile with preview, one directory sync job that also deprovisions users, and attribute-to-project rules. The OSS release adds the A2A Agent Gateway, GPT Live sessions, the Cloudflare Workers AI provider, and provider-injected MCP tools.

✨ Features

  • Access Profiles on Teams, Business Units and Customers - A team, business unit or customer can hold an access profile (GET/POST/DELETE /api/governance/{teams|business-units|customers}/{id}/access-profile). Members’ requests and the keys the entity owns are then governed and charged by that profile. An entity profile adds to a member’s own profiles and never removes access. Attaching to an entity that still has its own limits returns 409 entity_has_legacy_limits with a migration plan. POST .../access-profile/migrate (dry run supported) builds a profile from the old limits, carries spend over on the same reset window and adopts the entity’s keys. Assign Existing adopts a key into the entity profile. config.json accepts access_profile on governance.teams, governance.customers and governance.business_units. The entity sheets have Attach, Detach and Migrate actions, a migration wizard and a Usage tab. Docs
  • Business Unit Ownership and Limits - A virtual key can be owned by a business unit (business_unit_id). That key spends the unit’s budget and rate limit and those of the customers the unit serves. Business units have their own budget and rate limit (/api/governance/business-units/{id}/governance), and like teams they use either their own limits or a profile, never both. Deleting a business unit unassigns its keys in the database, in memory and on peers.
  • Business Unit and Customer Data Scopes - Roles can use two new data scopes. business-unit-data covers the caller’s business units, their members and keys owned by those units. customer-data covers every team and business unit under the caller’s customers, plus keys owned by those customers. Both scopes can be selected in the role editor and set in config.json.
  • Agent Access Grants - Access profiles and projects can grant Agent Gateway agents by name with agent_grants. On project update, omitting the field keeps the current grants, and an empty list clears them. Blank, duplicate and unregistered names are rejected. A2A bearer calls resolve the user through the IdP, and the agent log records the user with their team, customer and business unit. New RBAC resources are AgentGateway and AgentLogs, and agent log reads are DAC-scoped. Agents also appear in the Home, user detail and Activity views.
  • Home Page - Each user gets a Home page with:
    • spend, requests, top model, the budget closest to its limit, and spend rank;
    • cost and model charts, app usage, and a budget burn chart with a projection;
    • their keys and access. My Access shows disabled MCP servers and folds long tool lists.
    Endpoints: GET /api/governance/users/me and GET /api/governance/users/me/rank. A weekly token_monster badge goes to the top 10 spenders.
  • Budget Increase Requests - Users can request more budget on a virtual key or access-profile budget they own (/api/governance/users/me/budget-requests). Approvers with VirtualKeys:Update approve or deny requests under /api/governance/budget-requests, and a deny needs a note. On approval, override_strategy sets how the amount is applied: add (default), replace, or add_keep_lifetime. Approvers are notified, and each budget can have only one pending request. Home splits decided requests into Approved and Denied tabs.
  • Org Chart - New Governance page. It shows business units, teams and users, or customers, business units and teams, as a drill-down tree. Each level shows its top five children by spend, tokens, requests, budget or name. Nodes show a budget meter for the current cycle or a strip of the last 3, 6 or 12 cycles, and / jumps to any node. Every list is DAC-scoped (/api/governance/org-chart/*).
  • Activity Tab on Entity Sheets - Every entity detail sheet has an Activity tab. It shows spend against the previous period, tokens, requests, budget left, per-cycle budget history, spend by model, and models and MCP tools tables. Team sheets also rank members by spend.
  • Provider Key Holds in Adaptive Routing - When a provider refuses a key for a reason that will repeat (credential, model_access, quota, model_gone, region_blocked, rate_limit), that key is taken out of rotation for the affected route. The whole key is held only for a rejected credential or an org-wide quota refusal. After the wait, exactly one request goes through as a recheck. A clean answer releases the key. A failure moves it up a doubling backoff that starts at 30s, with caps from 5m to 30m by reason. A 429 holds the key for the provider’s Retry-After, which replaces the TPM circuit breaker. Holds are shared with cluster peers, survive restarts, and are fetched by joining nodes. GET /api/adaptive-routing/conditions lists holds, and POST /api/adaptive-routing/conditions/clear releases them across the cluster for one key, one provider, or all keys. Docs
  • Adaptive Routing History and Incidents - Each node records routing frames and events: health changes, weight changes, holds, settings edits, key edits and node boots. Storage is SQLite, Postgres or ClickHouse, with its own history_retention_days (1-30, default 7) and a history_enabled switch. The Adaptive Routing dashboard is rebuilt on this history:
    • provider, model, region and node filters, and a Live toggle;
    • a paged health timeline with a routing score breakdown;
    • held-keys and state sheets.
    A new Incidents page (/workspace/adaptive-routing/incidents) lists issues merged across nodes. Each issue shows its status, the recent config changes around it, nodes whose hold came from a peer, and whether an operator is needed. Read APIs are under /api/adaptive-routing/history/. The dashboard needs a Postgres or ClickHouse log store.
  • Routing Decision Metadata on Logs - Each attempt writes bifrost_alb_* metadata to its log: decision, requested provider and model, reroute target, excluded providers, key decision and skip reasons, failure class and region. An Adaptive Routing card on the log’s Routing tab shows these values, and the Logs sidebar has an Adaptive Routing filter group.
  • Adaptive Routing Settings - New reroute_target (pool, the default, or fallbacks). With fallbacks, a failed pinned request moves to its own first healthy fallback. A direction where only some keys fail is now Degraded and stays in rotation. snapshot_persist_interval_seconds is configurable, and its default changes from 30s to 5 minutes. A circuit breaker now follows a chain of open circuits to the first healthy target, with loop protection. Config and conditions routes move to /api/adaptive-routing/, and /api/load-balancer-config stays as a deprecated alias. UI labels are renamed: Reroute is now Redirect, and Prune Failed Fallbacks is now Skip Failing Fallbacks.
  • Identity Reconcile with Preview - POST /api/scim/provision/reconcile replays stored login claims or SCIM state, so a mapping edit reaches existing users. It accepts an optional user_ids subset. The preview reports per-user changes without writing anything: role, teams, business units, projects, access profiles, and keys revoked or created. .../reconcile/preview/user shows the plan for one user. After you save a mapping, the provider wizard offers to reconcile. The provisioning dashboard’s Re-verify action becomes “Reconcile users”.
  • Unified Directory Sync with Deprovisioning - The Sync users button, /api/scim/provision/sync and the scheduled run now execute one job. It creates or restores listed users and reconciles teams, business units, projects, roles and access profiles. After a complete listing, it deprovisions users the IdP no longer lists, and where the provider supports it, it first confirms the user is inactive. Service accounts and manual users are never touched. bulkSyncInterval (1h-30d, default 24h) applies without a restart. With SCIM provisioning on, bulk import and team or business-unit pulls return 403.
  • Attribute-to-Project Mapping - New attributeProjectMappings rules add users to existing projects from IdP attributes on login, refresh, directory sync and SCIM pushes. A sync removes only the members its rules added, and members a rule adds show a “Synced from IdP” badge.
  • Identity Mapping Rules - Several changes to how mapping rules work:
    • A glob rule value can contain more than one ${*}.
    • Role and access-profile rules are stored by entity ID, so renaming a role or profile no longer breaks them. A rule that names a missing entity is reported in skipped_mappings.
    • An interactive login now replaces claim memory, so an attribute the token omits removes what it granted. A login whose token has none of the role-rule attributes is refused with a 403 that names the missing attributes, and the user’s existing role is kept.
    • On a role tie, the rule higher in the list wins.
    • New SCIM group rules match on displayName.
  • SSO Provider Options - Every SSO provider accepts emailField, the token claim that holds the email (for example upn or a namespaced Auth0 claim). The generic OIDC provider accepts displayName for its login button. The bifrost-cli PKCE client is accepted, and CLI sessions do not use Edge device seats. Deploy this gateway before the OSS CLI release.
  • Alerting: Business Unit and User Scopes - Alert rules accept two new scopes. business_unit watches a unit’s budget and rate limit. user targets one user’s budget or, for user scope only, a rate_limit. Limits a user holds through access profiles and model configs are checked against their own targets and reset cycles. GET /api/alerting/users/{id}/targets lists the targets for a user, and the rule form adds a user picker and a limit picker. Rules are removed when their user or business unit is deleted. Docs
  • Alerting: Rules for All Entities with Digests - Every scope accepts scope_id: "*" (UI: Applies To > All). The rule then checks each virtual key, team, customer, business unit or user against its own limits. When several entities match in one pass, each channel gets one digest message, with rich layouts for Slack, Teams and PagerDuty. Cooldown and recovery are tracked per entity. History rows carry rule_scope_id, digest_id and digest_size, and the UI shows “All” and “Digest of N” badges.
  • Alerting: Access-Profile Budgets - Budgets held through access profiles now raise alerts.
  • Alerting: History Sorting and Paging - GET /api/alerting/history accepts sort_by (created_at, rule_name, channel_name, scope_type, status) and sort_order. The default is created_at descending. The history table sorts on the server and shows absolute and relative times. History defaults to a 7-day window, caps its count at 10,000+ (total_capped), and pages by keyset.
  • Guardrail Updates - New A10 TrojAI Defend provider (a10, with base_url and client_id). Other changes:
    • Profiles on a rule run in a set execution order.
    • The rule builder has a Builder / CEL toggle.
    • Detect-only matches on Regex, Secrets, Presidio, Azure PII, Lakera and Bedrock are recorded as detections.
    • Azure PII, Presidio and regex accept allowed_values, values that are never blocked or redacted (up to 10,000).
    • Bedrock assessments include confidence.
    Docs
  • Connector Export Updates - New export options for BigQuery, Datadog, Splunk, Kafka and Pub/Sub:
    • Per-category cost breakdown on all five. BigQuery adds 19 columns for it.
    • excluded_attributes drops listed attributes before export.
    • BigQuery export_raw_payloads writes provider bodies to raw_request/raw_response, and a new app column records the calling client.
    • Datadog metric_dimensions sends listed x-bf-dim-* dimensions as metric tags. Datadog and Splunk metrics carry user_id and user_name.
  • Audit Logs Without IP Addresses - New audit_logs.omit_ip_addresses stops storing client IPs. IPs in earlier rows are redacted on every read and export path. IP search and filters are disabled, and signatures stay valid. Docs
  • Warp in Enterprise - New Warp RBAC resource. Admin has View and Update, Developer and Viewer have View, and the log-index backfill needs Update. Warp follows the caller’s data scope, and describe_user_limits reports access-profile budgets and rate limits from live counters. Docs
  • RBAC Updates - New BackgroundJobs resource for /api/sidekiq/jobs: View to list, Update to cancel. Add, edit and delete actions across guardrails, prompt deployments, Edge approvals and access profiles are hidden or disabled when the role lacks the permission.
  • Bounded Audit Log Queries - Audit log search needs at least 3 characters and defaults to a 30-day window. Counts cap at 10,000+ (total_capped), and paging is keyset.
  • Edge Server Support - Admins can turn off Edge capture for one app, fleet-wide or per user or team (PUT /edge/interception with app_key). Edge Devices shows the installed Edge version. New /mcp/inspect/v2 lets MCP post-inspection run on any node, with no session affinity. Cursor config accepts per-operation models (linter_model, embedding_model, speech_model, image_model, video_model, transcription_model and others). Docs
  • MCP in Clusters - MCP server instructions are saved and sent to peers with discovered tools. Refreshing a client’s tools makes every peer re-discover them right away.

🌎 Open Source Features

  • Agent Gateway (A2A) - Bifrost proxies A2A agents. Register an agent by its card URL (POST /api/agents or the config.json agents array). Clients reach it over JSON-RPC (with SSE streaming), REST, or an optional shared gRPC listener. It supports header, OAuth and TLS upstream auth, and push notifications through a durable, retried outbox. Access is granted per virtual key with agent_grants. Every operation is logged in a new Agent Logs workspace.
  • Warp AI Assistant (alpha) - In-dashboard assistant that answers questions about logs, spend, latency and governance with read-only tools, inline charts and links to matching log rows. Completed logs can be embedded for semantic search, with a resumable backfill. Warp is off by default in Enterprise (warp feature flag). Docs
  • GPT Live Sessions - Proxies OpenAI GPT Live over WebSocket and WebRTC at /v1/live/sessions, with sideband attach and recording download. Voice is billed in 30-second windows, governance is checked at each window, and live is its own allowed request type.
  • Cloudflare Workers AI Provider - New cloudflare provider for chat (streaming included), Responses, embeddings and model listing. Each key needs cloudflare_key_config.account_id. A dialog in the UI migrates an existing custom provider named cloudflare. Docs
  • Provider-Injected MCP Tools - A provider’s injected_tools adds MCP tools to every chat and Responses request for that provider. Bifrost runs them server side and returns only the final answer. An entry named web_search replaces native web search. Configure it in the provider’s new Tools tab.
  • OAuth-Minted Keys for OpenAI-Compatible Providers - oauth_key_config mints a bearer token with the client_credentials or jwt_bearer grant, caches it, and refreshes it before expiry. Databricks and GitHub Copilot use the same token cache.
  • Decisions API Updates - /v1/decisions takes OpenAI-shaped input and questions, and new /openai/v1/decisions routes accept the OpenAI SDK request. Native decision models are reached on OpenAI, Perplexity, Ollama and OpenRouter. TypeSafe can be a custom provider base, and decisions have their own pricing fields. Emulation on non-decision models is now opt-in through convert_decision_to_responses. Docs
  • Complexity Router Decision Classifier - The complexity router can classify with a decision model (TypeSafe Jev by default), as the primary classifier or as a fallback. Per-tier guidance is editable. Docs
  • Multimodal Embeddings - /v1/embeddings accepts text, image, audio, video, file and token parts, served by Cohere, Gemini, Vertex and Bedrock (Nova and Titan multimodal). Parasail now serves embeddings.
  • Responses Tools - Adds OpenAI shell and apply_patch tools, maps allowed_callers between OpenAI and Anthropic, and maps Gemini code execution to code_interpreter_call.
  • Retrieve a Single Model - GET /v1/models/{model} for OpenAI, Anthropic, Cohere, Gemini, Mistral and Ollama, with shutdown_date where the provider announces one.
  • xAI Realtime - xAI is a realtime voice provider on the WebSocket gateway, with client-secret minting. Docs
  • Bedrock Passthrough - /bedrock_passthrough forwards InvokeAgent, knowledge-base Retrieve and ApplyGuardrail, SigV4-signed with the key’s AWS credentials. Docs
  • Vertex Workload Identity from AWS - vertex_key_config.aws_workload_identity authenticates from EKS IRSA, Pod Identity, ECS task roles or EC2 instance profiles, with no stored key.
  • Pricing Updates - Web search is billed per request (web_search_cost_per_request), and existing values are migrated. *_above_100k_tokens rates apply above 100,000 prompt tokens. A per-provider ignore_provider_cost setting prices requests from the catalog instead of the provider’s reported cost.
  • Compat Updates - force_reasoning_only_models_to_responses (default true, also turned on for existing deployments) sends tool requests for reasoning-only models to the Responses API. Chat accepts reasoning.mode and reasoning.summary.
  • Failure Classification and Key Rotation - Each failed attempt gets a failure_class. Credential and quota failures hold a key out for every model, and permanent per-key failures move to the next key even at max_retries: 0. Provider retry hints are exposed as extra_fields.retry_after_ms.
  • Routing Updates - Routing targets accept ttft_timeout_ms, which hands a slow-to-start stream to the next fallback. When a rule or router rewrites the route, the original is returned in routing_info.requested_provider/requested_model and response headers. Every traced response carries x-bifrost-request-id.
  • Provider Objects Bound to the Creating Virtual Key - Files, batches, videos, containers and cached contents can be read only through the virtual key that created them. Turn this off with client.enforce_vk_object_ownership (default true). File-based batches are checked against the key’s model allowlist.
  • Security Hardening - Several changes to defaults and status codes; review them before upgrading:
    • Passthrough routes forward only POST requests to allowlisted inference paths.
    • With dashboard auth off, browser requests to /api/* from unlisted origins return 403.
    • Secret references, private webhook targets, plugin endpoints and Vertex auth_credentials need real auth.
    • Re-enabling auth while it is off needs current_password or a setup_token.
    • Compressed bodies may expand at most 100 times.
  • Per-Virtual-Key Content Logging and Auto-Delete - Virtual keys have a tri-state disable_content_logging, and delete_after_expire deletes a key once it expires. Docs
  • Global Proxy for All Outbound Traffic - proxy_config can be set in config.json and Helm. With enable_for_api, MCP, catalog sync, OTel, webhooks and plugin downloads use it. With enable_for_inference, providers without their own proxy now actually send inference through it.
  • MCP Updates - Tools can be refreshed on demand (POST /api/mcp/client/{id}/refresh-tools), and dropped sessions reconnect automatically. An evicted OAuth client can be re-registered. Upstream server instructions are forwarded and injected, with size caps. mcp_oauth_private_allowlist allows private OAuth servers. Rotating a virtual key revokes its MCP OAuth grants. Code mode runs on an in-process Starlark runtime with code_mode_limits. Docs
  • Semantic Cache Updates - Entries are scoped by identity (virtual key plus user), so expect a cold cache after upgrading. Responses with client tool calls are not cached unless cache_tool_call_responses is on. Lookups add x-bifrost-cache-* response headers. Docs
  • Observability Updates - New trace data and metric changes:
    • Root spans carry error attributes and an end-to-end TTFT.
    • OTel profiles can export raw payloads, drop listed attributes, and carry a bifrost.cost.* breakdown.
    • Metrics get an app label.
    • The root span uses the stable OTel HTTP attributes.
    • Tracing overhead is lower when no connector is attached. Docs
  • Response Headers Plugin Hook - New HTTPTransportResponseHeadersHook lets plugins set headers on buffered and streaming responses. Custom Go plugins must add it (a no-op is fine).
  • Logs and Dashboard - New views and controls:
    • Grouped logs collapse requests by session_id.
    • A Background jobs panel lists and cancels long jobs.
    • object_storage_exclude_request_types keeps chosen request types in the database instead of object storage.
    • Provider and model pickers are unified, with server-side search.
    • Entity pickers scroll infinitely.
  • Bifrost CLI - The bifrost CLI covers the published API, with resource commands, chat, infer, auth (SSO included), usage, reversible configure for native agents, and table, JSON or YAML output.
  • Helm Chart 2.1.44 - Exposes the new config fields, topologySpreadConstraints and extraContainers.

⚙️ Upgrade Notes

These entries change a default, a status code, a response shape or a Go SDK type. The matching entries above describe each change in full.
  • Agent Gateway (A2A) - optional; set client.a2a_external_client_url for push and stable card URLs, and server.a2a_grpc_base_domain + server.a2a_grpc_port (plus wildcard DNS) for gRPC. Custom schemas.Tracer implementations must add ReleaseTrace.
  • Warp AI Assistant (alpha) - on by default in OSS; set feature_flags.flags.warp.enabled: false to keep it hidden.
  • Cloudflare Workers AI Provider - the name cloudflare is reserved; migrate a custom provider named cloudflare with the dashboard dialog or recreate it with cloudflare_key_config.account_id.
  • Decisions API in OpenAI Shape - map-form /v1/decisions bodies are deprecated (still served); update Go SDK code to the new BifrostDecisionRequest fields.
  • Decision Emulation Is Opt-In - decisions on non-decision models return unsupported_operation; set compat.convert_decision_to_responses: true or send x-bf-compat: ["convert_decision_to_responses"] to keep emulation.
  • Multimodal Embeddings - Go SDK BifrostEmbeddingRequest.Input is now []EmbeddingInputItem; HTTP bodies are unchanged.
  • xAI Realtime - refusal and content-filter stops are logged as model_refusal errors; exclude that error type from error-rate alerts if needed.
  • Web Search Priced per Request - rename search_context_cost_per_query to web_search_cost_per_request in config.json overrides; read counts from usage.tool_usage.web_search.num_requests.
  • Compat: Route Reasoning-Only Models to Responses - turned on for existing deployments; set compat.force_reasoning_only_models_to_responses: false to keep the old behavior.
  • Chat reasoning.mode and reasoning.summary - the /openai chat route strips reasoning.summary; send it through extra_params there.
  • Failure Classification and Smarter Key Rotation - a model missing on every key returns the provider’s 404 instead of 502 upstream_credentials_exhausted; update alerts on that error and on fail_reason values.
  • Gateway Request ID Header - read the gateway request ID from x-bifrost-request-id; upstream x-bifrost-* headers are no longer forwarded.
  • Deprecated Models Hidden from Browsing - add include_deprecated=true to GET /api/models to keep deprecated models.
  • Provider Objects Bound to the Creating Virtual Key - set client.enforce_vk_object_ownership: false if virtual keys share files, batches, containers or cached contents.
  • File-Based Batches Under Model-Restricted Virtual Keys - use a * model grant for OpenAI/Azure file batches, send an allowed model for Gemini/Vertex/Bedrock file batches, and never mix a file with inline requests.
  • Passthrough Route Hardening - use passthrough only for POST inference paths with rooted paths, the route’s own provider and one string model key.
  • Management API Refuses Unlisted Browser Origins - add cross-origin browser apps to client.allowed_origins or enable dashboard auth.
  • Real Auth Required for Secret References and Private Targets - enable dashboard auth or send X-Bifrost-Setup-Token for MCP env./vault. references, private webhooks, plugin endpoints, Vertex auth_credentials and DELETE /api/mcp-logs.
  • Auth Config Changes Need Proof of Control - send auth_config.current_password or setup_token when re-enabling auth or rotating admin credentials while auth is off.
  • Request Decompression Limits - bodies expanding over 100x return 413; send them uncompressed or raise client.max_request_body_size_mb.
  • Access Profiles for Teams, Business Units and Customers - move config.json limits of governed entities and their keys onto the access profile.
  • Global Proxy in config.json and for All Outbound Traffic - set enable_for_inference: false to keep inference direct, and list direct hosts in proxy_config.no_proxy.
  • Audit Logs: Omit IP Addresses - make SIEM rules accept both audit typeURI values.
  • Attribution Context Keys Deprecated - read attribution in custom plugins from ctx.Grant().
  • MCP Client Target Validation at Connect Time - register private MCP servers as an authenticated admin or in config.json, and use http, sse or stdio as connection_type.
  • MCP OAuth Server Hardening - list remote OAuth callback URIs in client.oauth2_server_config.allowed_redirect_uris before upgrading.
  • Virtual Key Rotation Revokes MCP OAuth Grants - re-authorize MCP clients after rotating their virtual key.
  • MCP Server Instructions - bound injected instructions with max_instructions_per_client, max_instructions_total or per-client max_instructions_length.
  • Code Mode Execution Limits - raise fields under mcp.tool_manager_config.code_mode_limits for scripts that exceed the new budgets.
  • Semantic Cache Scoped by Identity - expect a cold cache; set disable_identity_scoping: true to share entries across identities.
  • Semantic Cache Skips Tool-Call Responses by Default - set cache_tool_call_responses: true to keep caching them.
  • App Label on Metrics - add the new app label to recording rules or alerts that match exact label sets.
  • OTel HTTP Semantic Conventions - move dashboards to http.request.method, http.response.status_code, http.route and url.path; the span name and http.url drop the query string.
  • Response Headers Plugin Hook - Go types implementing HTTPTransportPlugin must add HTTPTransportResponseHeadersHook (a no-op is fine).
  • Exclude Request Types from Object Storage - only exclude request types whose content you do not need retained in object storage.
  • Helm Chart 2.1.44 - set bifrost.client.oauth2ServerConfig.issuerUrl when mcpServerAuthMode is oauth or both, or helm template fails.
  • Outbound Fetch Redirect Hardening - test-fire private webhooks with an authenticated caller; point datasheet URLs at their final location or use file://.
  • Provider base_url Shape Checked - remove userinfo, query strings and fragments from base_url.
  • Direct Keys Stay on Their Provider - pass ?provider= or x-bf-model-provider on direct-key model listings.
  • config.json Governance Limits Reconciled Correctly - read virtual key and provider config limits from the key’s model configs after the migration.
  • Partial Provider Key Updates - send a field explicitly to clear it.
  • Weights Must Be Valid Numbers - use weights of 0 or more for keys and greater than 0 for routing targets.
  • Duplicate Routing Rule Priority Returns 409 - treat 409 as a request error instead of retrying on 500.
  • Dashboard Rankings Under DAC - treat the ranking row id: "other" like unassigned.
  • MCP OAuth and Client Fixes - expect 404, not 500, for an unknown MCP client.
  • Implicit Prompt Caching for gpt-5.6+ Now Takes Effect - set prompt_cache_options.mode: "explicit" on the request to opt into explicit mode.
  • Stream Errors Get a Real HTTP Status - update retry logic and alerts that assumed every stream error was 400.
  • Bedrock Incomplete Converse Streams - truncated Converse streams now end with 502.
  • Replicate File List Cursor - pass the after cursor exactly as returned; others get 400 invalid pagination cursor.
  • Anthropic Error and Stop Fidelity - branch on documented Anthropic error types instead of api_error.
  • Error Metrics Report the Real Status - repoint alerts on status_code="unknown" and add error_type to OTel queries.
  • OTLP HTTP Metrics to https Endpoints - set tls_ca_cert or keep insecure: true for self-signed collectors.
  • Connector Export Gaps - group OTel datapoints by bifrost_instance_id instead of service_instance_id, and review telemetry custom_labels for cardinality.
  • Postgres Scale Fixes - plan IO for the background index build; set statement_timeout and idle_in_transaction_session_timeout to "0" to keep server defaults.
  • ClickHouse Large Team Scopes - set logs_store.config.max_query_size if a ClickHouse settings profile constrains it.
  • Semantic Cache Vector Store Consistency - on Weaviate, set threshold to 2 * old - 1 to keep the old hit rate.

🐞 Fixed

  • DAC Leaks Closed - Each of these routes now returns 404, 403 or a filtered list:
    • log and MCP log deletes, and log cost recalculation;
    • user sub-resources;
    • budgets and rate limits lists, from_memory=true governance reads, and governance introspection;
    • prompt versions and sessions, and routing rule deletes;
    • org link removal and nested child lists, team replace, and key owners;
    • access profile history.
  • DAC Visibility Rules - Team member counts, model config lists and org entity creator visibility follow the caller’s scope. A member’s key is visible only when it is attached to that member. Callers with more than 65,535 members or keys no longer get errors on scoped reads.
  • One Owner per Virtual Key - A key attached to a team, business unit or customer can no longer also be attached to a user, and the reverse. Both return 409.
  • Governed Member Key Ownership - A governed member’s new key stays under their own access profile, whatever entity the request body names.
  • Entity Profile Fixes - “All providers” entity profiles resolve unprefixed models. config.json limits detach a conflicting profile. The profile-or-limits guard covers every write path. Entity-governed keys are hidden from other pickers.
  • Agent Governance for IdP Users - Callers with an IdP token now record a credential, so deny-by-default Agent checks run for them.
  • Deleted Users Lose Desktop Sessions - Deleting or deprovisioning a user deletes their desktop sessions and evicts cached tokens across the cluster.
  • IdP Secret Rotation to Env or Vault - Switching an IdP secret to an env. or vault. reference is now saved.
  • SCIM Protocol Fixes - Changes for RFC 7644 and IdP compatibility:
    • GET /Users evaluates RFC 7644 filters and pages.
    • Core roles and entitlements are stored, and PATCH active:true reactivates a user. Entra’s "True"/"False" strings are accepted.
    • PATCH identity edits are kept, and a PUT clears omitted attributes.
    • Extension attribute names match in any case, and multi-valued PATCH follows RFC semantics.
    • A DELETE clears stored identity data.
  • SCIM Group Fixes - A group push updates every team and business unit its rules name. A rename reloads memory and peers. A DELETE keeps memberships a user attribute still grants. Entra object-ID/name pairs are matched, and group pushes take over attribute-created memberships.
  • Directory Pull Safety - Background pulls no longer restore deleted users or grant Admin. Users keep group-granted memberships when a group lookup fails. Okta and Entra records carry the same claims a login does. Keycloak subgroups are walked, and Entra users in more than 200 groups are resolved.
  • Group Naming - A team or business unit named after a raw group ID is renamed in place instead of forked. Entra business units use the group’s display name. Two rules on one group value no longer rename one team back and forth. Retargeted exact rules find or create the new target.
  • IdP Rate Limits - Okta admin reads retry until X-Rate-Limit-Reset, and Entra group lookups retry on 429 and 5xx and cache resolved names for 10 minutes.
  • SSO Token Handling - Fixes:
    • Entra SSO works without a cloud key.
    • Inline refresh works for Auth0, Generic, Zitadel and SailPoint.
    • A refresh without an ID token no longer loops.
    • Logout revokes the access and refresh tokens.
    • ID-token verifiers are cached.
    • The inference JIT path uses the configured email claim.
  • Token Exchange Subject - A valid IdP JWT sent with a virtual key under prefer_vk, or behind an auth proxy, is recorded as the token-exchange subject. This fixes subject_token_missing.
  • Mapping Rules on First Boot - SCIM mapping reconcile now runs after roles and access profiles load from config.json.
  • Adaptive Routing Accuracy - Cancelled, timed-out and caller-fault requests no longer count against a key. Caller x-bf-direct-key failures are not held. Caller key pins no longer follow a request to its fallback. Idle failed routes move to Recovering. Deleted keys and providers are purged on every node, and editing a key releases its holds.
  • Adaptive Routing Cluster State - Hold timestamps keep nanosecond precision over gossip. Only the global leader writes the hold table. Restored samples keep their original timestamps.
  • Load Balancer Memory - The tracker uses a chunked in-memory store and keeps 1h of history (was 24h), and key selection no longer copies route metrics on every request.
  • Alerting: Rules Match Only Entities with the Named Limits - A rule no longer fires for entities that do not hold the limits it names. An idle reset no longer sends the notification again, and digest counts no longer repeat entities.
  • Alerting: Per-Rule Channel Cooldowns - Cooldowns are tracked per channel and rule, so one rule no longer silences another on a shared channel. Skip history rows are throttled, CEL evaluation errors are recorded, and webhook Authorization headers are kept.
  • Alerting: Channel Delete Guard - Deleting a rule’s last channel returns 409 alert_channel_in_use with the blocking rules. A rule needs at least one channel, and a PUT keeps fields the body leaves out.
  • Alerting: Channel Edit Keeps Headers - Editing a channel keeps its stored headers unless “Remove existing headers” is on, and delete conflicts are shown in the dialog.
  • Alerting: User-Scoped Alert History - Alerts with user scope now write a history row, so they show in the UI.
  • Connector Fixes - Datadog metrics no longer depend on trace export, and an unreachable agent no longer blocks startup. Disabled connectors stay disabled after restart. Pub/Sub excluded_attributes applies on the live publisher. Splunk keeps zero costs. Kafka, Pub/Sub and Splunk strip all content attributes when content logging is off.
  • BigQuery Writes - Flushes are split under 9 MiB, an oversized row is dropped on its own, and latency keeps sub-millisecond precision.
  • Project Fixes - Project create rejects unknown or duplicate Virtual MCP IDs with a clear error, and calendar_aligned updates per-model budgets.
  • Business Unit Fixes - Clearing one rate limit keeps the other. Deleting a unit removes its customer links on SQLite. Budget columns include active overrides.
  • Late Log Redaction - Late responses written through batch updates are redacted like new inserts.
  • Plugin Reload Context - Plugins reloaded through the API use the server context, not the request context.
  • Startup Migrations - Startup runs only enterprise migration steps not yet recorded.
  • Fallback Billing and Calendar Alignment (OSS) - Fallback usage is billed after a billed primary. Calendar-aligned team and virtual key budgets keep their alignment after restart.
  • Outbound Fetch Redirect Hardening (OSS) - Datasheet, MCP library, OAuth and webhook fetches enforce the private-network policy on every redirect hop.
  • Encrypted Session and Virtual Key Lookups (OSS) - The plaintext fallback matches only plaintext rows.
  • Virtual Key Header Redacted in Traces (OSS) - x-bf-vk is redacted in captured headers.
  • Direct Keys Stay on Their Provider (OSS) - x-bf-direct-key is no longer sent to every provider on an unscoped model listing.
  • Provider Proxy on Every Egress Path (OSS) - Side fetches, credential exchanges and Bedrock runtime calls use the provider’s proxy_config.
  • config.json Governance Limits (OSS) - Inline virtual key and provider config limits are stored on model config tiers and update on reload.
  • Virtual Key and Provider Key Partial Updates (OSS) - PUT without key_ids keeps key associations, and partial key updates keep omitted fields.
  • Session Affinity Rebinds on Refused Keys (OSS) - A session bound to a key the provider refuses moves to another eligible key.
  • Caller Errors Skip Inferred Fallbacks (OSS) - A 4xx caller error is returned instead of being answered by a catalog-inferred fallback.
  • Implicit Prompt Caching for gpt-5.6+ (OSS) - Requests with cache_control markers stay in implicit mode instead of rewriting the conversation at the cache-write rate.
  • Stream Errors Get Real Status Codes (OSS) - Mid-stream errors return 429, 400, 502 or 529 instead of 400. Truncated Bedrock Converse and Content-Length streams are reported as errors.
  • Anthropic and Bedrock Fidelity (OSS) - Anthropic error types and stop_sequence are preserved. Bedrock guardrail guardContent, document cache points and Converse reasoning replay are kept. Safeguard verdicts reach Claude Code.
  • Gemini and Vertex Fixes (OSS) - Function tools without a schema are declared, and parallel tool results are reordered. n maps to candidateCount. Extra params survive retries.
  • Postgres Scale Fixes (OSS) - Chunked log inserts, composite owner indexes, batched retention, and retried batches on connection errors. Logs with binary content or NUL bytes no longer fail a batch.
  • ClickHouse Fixes (OSS) - Large team scopes no longer exceed max_query_size, and time filters keep millisecond precision.
  • Model Listing Robustness (OSS) - One unreachable provider no longer blocks GET /v1/models.
  • Prometheus Fixes (OSS) - Custom labels no longer collide. Cancelled requests are billed. Errors report the real status code.
  • Semantic Cache Fixes (OSS) - Entries are isolated by request family, and the cache write no longer races core cleanup.

🐙 Closed OSS Issues

  • #2649 - [Feature]: OpenAI Responses API ShellTool not supported
  • #3471 - [Bug]: Bedrock Titan embedding concatenates list inputs instead of fanning out - silent corruption and 400 on large batches
  • #3632 - [bug]: bedrock - Titan multimodal embedding model rejected as unsupported
  • #6143 - [Bug]: data race - fasthttp requestStream released to pool while SSE reader is still inside Read (stream cancellation)
  • #6477 - [Bug]: MCP OAuth reauthorization prematurely closes popup on first poll tick and rejects completion with 409 (stale “authorized” status in oauth_configs)
  • #6612 - [Bug]: A single unreachable provider blocks the aggregate GET /v1/models (re-polled live every request; bounded only by the shared request timeout)
  • #6742 - [Feature]: Surface DeepSeek’s silent model substitution and validate usage completeness on its Anthropic endpoint
  • #6870 - [Bug]: Failed live MCP connection checks never trigger the documented reconnect
  • #6885 - [Bug]: MCP tool lists never refresh when a server’s tool set changes - notifications/tools/list_changed is ignored, and per-call clients have no refresh path at all
  • #7191 - [Bug]: MCP OAuth authorization is lost and manual re-login does not recover it
  • #7233 - [Bug]: Direct-only cache response serialization races with core cleanup and can panic
  • #7275 - [Bug]: gpt-6-astra / Foundry: tools + reasoning on /v1/chat/completions still 400 after #5231
  • #7341 - [Bug]: Gemini Responses stream - response.completed.Output is empty when parts and finishReason share one frame
  • #7342 - [Bug]: Gemini Responses stream drops a generated image part that carries a thoughtSignature
  • #7344 - [Bug]: strict, allowed_tools, parallel_tool_calls and allowed_callers are dropped in provider conversion
  • #7347 - [Bug]: PUT /api/governance/virtual-keys/{id} silently sets allow_all_keys=false and drops key associations when key_ids is omitted (GET does not return key_ids)
  • #7388 - [Bug]: Gemini & Vertex Providers: Missing N Parameter Mapping to CandidateCount in Chat Completions
  • #7415 - [Feature]: Support OpenRouter’s native Decisions API
  • #7438 - [Bug]: otel HTTP telemetry uses deprecated semconv; no http.route or http.server.request.duration
  • #7446 - [Bug]: otel HTTP metrics exporter ignores https scheme when insecure is omitted (defaults true)
  • #7450 - [Bug]: Semantic cache panics while marshaling /v1/responses
  • #7560 - [Bug]: semantic_cache serves a chat.completion entry to a /v1/responses caller because the direct cache key omits the request family
  • #7572 - PUT /api/providers/{provider}/keys/{key_id}: partial update silently wipes key value and weight (destructive merge default)
  • #7580 - [Feature]: Allow OpenAI reasoning.mode (“pro”) on Chat Completions and carry it through chat→responses conversion
  • #7581 - [Bug]: Complexity Router: fails to find provider config unless provider name is all-lowercase
  • #7584 - UI: Recharts warns width(-1)/height(-1) on Dashboard, LLM Logs and MCP Logs
  • #7599 - [Bug]: TypeSafe native compatibility rejects SDK-valid inputs and loses payload/response fidelity
  • #7601 - [Bug]: /v1/responses for Anthropic and Gemini omits status and does not signal max_output_tokens truncation
  • #7610 - Provider harness: group 25 (DeepSeek Anthropic-Compatible Migration) runs on the OpenAI-compatible endpoint - is use_anthropic_endpoints meant to be set?
  • #7613 - [Bug]: Bedrock Converse drops cache_control on file/document content blocks
  • #7634 - Access profile summary shows “All models” when blocked models are configured
  • #7649 - [Bug]: Bedrock provider drops extended-thinking token count (output_tokens_details.thinking_tokens) that AWS returns
  • #7659 - [Bug]: mirrored reasoning params (reasoning_effort + reasoning.effort, equal values) rejected as 400 Invalid request payload
  • #7661 - [Bug]: Reasoning Parameters renders an unlabeled 0 for reasoning.max_tokens
  • #7693 - [Bug]: streamed chat completions omit delta.role when the request is converted to the Responses API
  • #7694 - [Bug]: Gemini rejects tool results that contain $ref - JSON-object tool output is forwarded as structured function_response.response
  • #7696 - [Bug]: Bedrock guardrail selective evaluation is impossible - guardContent blocks silently stripped in canonical round-trip; guardrailConfig has no tagSuffix
  • #7720 - [Bug]: Bifrost ignores “store”: false in additionalModelRequestFields on the Bedrock Converse route, so store cannot be disabled for Azure
  • #7729 - [Bug]: Bifrost replaces the reasoning item id when a Bedrock Converse request replays redactedContent, so Azure rejects it and Bifrost strips the reasoning
  • #7760 - ListAllModels returns duplicate and skipped models when a provider listing changes between pages
  • #7768 - [Bug]: bare Claude model ids hoist thinking blocks on the Bedrock path
  • #7784 - [MCP Library] Parlel - free people/companies/jobs/agents MCP
  • #7812 - [Bug]: Gemini embeddings: taskType/title applied only to the first input of a batched /v1/embeddings request
  • #7819 - [Bug]: Transcription cost ignores provider-reported usage.cost and per-second pricing is unreachable when input_tokens > 0
  • #7826 - [Bug]: Gemini and Vertex image and transcription requests lose ExtraParams on retry
  • #7843 - [Bug]: Postgres log store batch inserts exceed the 65,535 bind-parameter limit, so full batches fall back to row-by-row inserts
  • #7915 - [Bug]: Anthropic-compatible /anthropic/v1/messages returns wrong or non-standard error.type
  • #7920 - [Bug]: Anthropic /v1/messages never reports stop_reason “stop_sequence” or the matched stop_sequence
  • #7926 - [Bug]: Gemini/Vertex drop function tools without input_schema and mis-pair parallel tool results
  • #7990 - [Bug]: Responses history gains logprobs: null, causing upstream 400
  • #8004 - [MCP Library] Add: FXMacroData
  • #8153 - [Bug]: Routing Tree silently truncates routing rules at 100
  • #8171 - [Bug]: Routing Tree CEL parser splits and rewrites operators inside quoted string literals
  • #8195 - [Bug]: Anthropic tool_result.is_error no longer reaches OpenAI Responses upstreams - the status: "incomplete" set by the Anthropic converter is cleared in ToOpenAIResponsesRequest (#7835)
  • #8196 - [Bug]: /v1/responses rejects multi-part assistant messages - output_text is not mapped to text in ToChatMessages
  • #8205 - [Bug]: ClickHouse log list returns full input histories
  • #8207 - [Bug]: Anthropic tool_addition / tool_removal blocks lose their tool object in the Responses conversion
  • #8212 - [Bug]: Log retention changes do not update the running cleaner
  • #8215 - [Bug]: DeepCopyChatMessage drops cache_control, citations, prompt_cache_breakpoint and cachePoint on chat content blocks
  • #8246 - skills_registry: config-driven skill updates always fail - populateSkillFiles resolves the serving version via skill.LatestVersion, which configEntryToTableSkill never sets
  • #8266 - [Bug]: calendar-aligned virtual key budgets reset on a rolling window after a restart
  • #8286 - [Bug]: Fixed-length streamed responses silently accept premature EOF

📀 Base OSS version

transports/v2.3.0 (pinned as github.com/maximhq/bifrost/transports v1.6.12-0.20261013033355-a014d2db10df, the v2.3.0 tag plus its changelog commit), with core v1.12.0, framework v1.9.0, governance v1.9.0, and logging v1.9.0.

🔌 If you are compiling plugin against this release - use following deps