Skip to main content
v1.5.0 introduces several breaking changes across provider key configuration, Virtual Key semantics, the Go SDK, and the REST API. This page consolidates every breaking change with before/after examples and a migration checklist.
Make a database backup before upgrading. Automatic database migrations run on startup and are not revertible. A backup is the only way to restore a previous state if anything goes wrong. A database successfully migrated to v1.5.0 cannot be used to run v1.4.x.

Automatic Database Migration

If you are running Bifrost with a database (SQLite or Postgres), existing data is automatically migrated on startup. You do not need to manually update your database records. The following automatic migrations run on upgrade:
  • Provider keys with models: [] are converted to models: ["*"]
  • Virtual Key provider configs with allowed_models: [] are converted to allowed_models: ["*"]
  • Virtual Keys with no provider_configs are backfilled with all currently configured providers (allowed_models: ["*"], key_ids: ["*"])
  • Virtual Keys with no mcp_configs are backfilled with all currently connected MCP clients (tools_to_execute: ["*"])
  • Per-provider deployments maps (Azure, Bedrock, Vertex, Replicate) are migrated into the unified aliases field
The automatic migration only protects your existing data. Any new configuration created after upgrading - via config.json or the REST API - must follow the new semantics described below.

Breaking Change 1: Empty Array Now Means “Deny All”

v1.5.0 flips the meaning of empty arrays across all allow-list fields:
What you writev1.4.x meaningv1.5.0 meaning
[] (empty array)Allow allAllow none
["*"] (wildcard)Not applicableAllow all
["a", "b"]Only a and bOnly a and b (unchanged)
This affects four fields:
FieldWhere
modelsProvider key
allowed_modelsVirtual Key provider config
key_idsVirtual Key provider config
tools_to_executeVirtual Key MCP config

Provider key models

Before:
{ "value": "env.OPENAI_API_KEY", "models": [] }
models: [] → key served all models After:
{ "value": "env.OPENAI_API_KEY", "models": ["*"] }

Virtual Key allowed_models

Before:
{ "provider": "openai", "weight": 1.0 }
Missing allowed_models → all models allowed After:
{ "provider": "openai", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }

Virtual Key MCP tools_to_execute

Before:
{ "mcp_client_name": "my-tools", "tools_to_execute": [] }
After:
{ "mcp_client_name": "my-tools", "tools_to_execute": ["*"] }

Breaking Change 2: allowed_keys Renamed to key_ids

The field used to restrict which provider API keys a Virtual Key can use has been renamed from allowed_keys to key_ids. The deny-by-default rule also applies - omitting the field or setting it to [] now blocks all keys.
Unlike allowed_models, there is no automatic database migration for key_ids. An empty or omitted key_ids disables all key selection. You must explicitly use ["*"] to restore allow-all behavior.
Before:
{ "provider": "openai", "allowed_keys": ["key-prod-001"], "weight": 1.0 }
After:
{ "provider": "openai", "key_ids": ["key-prod-001"], "allowed_models": ["*"], "weight": 1.0 }
To allow all keys:
{ "provider": "openai", "key_ids": ["*"], "allowed_models": ["*"], "weight": 1.0 }

Breaking Change 3: Virtual Key provider_configs is Deny-by-Default

In v1.4.x, a Virtual Key with no provider_configs had access to all providers. In v1.5.0, it blocks all providers. Before: "provider_configs": [] → access to all providers After: "provider_configs": [] → no provider access To allow all providers, list each one explicitly:
{
  "provider_configs": [
    { "provider": "openai",    "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 },
    { "provider": "anthropic", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }
  ]
}
The automatic migration backfills all currently configured providers into any VK that has an empty provider_configs. However, any VK created after upgrading must include explicit provider configs.

Breaking Change 4: WhiteList Validation

Two new validation rules are enforced on all allow-list fields. The API returns HTTP 400 if either is violated. Rule 1: Wildcard cannot be mixed with other values
// ❌ Invalid
{ "allowed_models": ["*", "gpt-4o"] }

// ✅ Valid
{ "allowed_models": ["*"] }
Rule 2: No duplicate values
// ❌ Invalid
{ "allowed_models": ["gpt-4o", "gpt-4o"] }
Applies to: models, allowed_models, key_ids, tools_to_execute, tools_to_auto_execute, allowed_extra_headers.

Breaking Change 5: weight is Now Nullable

The weight field on a Virtual Key provider config was previously a required float64. It is now an optional *float64.
  • weight: 0.5 - provider participates in weighted load balancing
  • weight: null / omitted - provider is accessible for direct routing but excluded from weighted selection
API response change: weight may now be null. Update any client code that assumes it is always a number.

Breaking Change 6: Virtual Key budget Changed to Multi-Budget budgets

The budget model on Virtual Keys and provider configs has been restructured from a single budget to support multiple budgets with different reset intervals. In v1.4.x, a Virtual Key had a single budget_id foreign key pointing to one budget, and each provider config also had a single budget_id. In v1.5.0, the association is inverted: budgets now reference their parent via virtual_key_id or provider_config_id, and both Virtual Keys and provider configs support an array of budgets. The database migration runs automatically on startup, converting existing single-budget associations into the new multi-budget structure.

API request changes

Creating a Virtual Key with a budget: Before:
{
  "name": "my-key",
  "budget": {
    "max_limit": 100.0,
    "reset_duration": "1d"
  }
}
After:
{
  "name": "my-key",
  "budgets": [
    { "max_limit": 100.0, "reset_duration": "1d" },
    { "max_limit": 500.0, "reset_duration": "1M" }
  ]
}
Provider config budgets: Before:
{
  "provider": "openai",
  "budget": { "max_limit": 50.0, "reset_duration": "1h" }
}
After:
{
  "provider": "openai",
  "budgets": [
    { "max_limit": 50.0, "reset_duration": "1h" }
  ]
}

API response changes

Fieldv1.4.xv1.5.0
Virtual Key budget_idPresentRemoved
Virtual Key budgetSingle objectRemoved - replaced by budgets array
Virtual Key budgetsNot presentArray of budget objects
Provider config budget_idPresentRemoved
Provider config budgetSingle objectRemoved - replaced by budgets array
Provider config budgetsNot presentArray of budget objects
calendar_aligned has moved from the budget level to the Virtual Key level. It now applies uniformly to all budgets under a VK.

Viewing budget for a given key

Budgets are returned as part of the Virtual Key response on all admin endpoints:
  • GET /api/governance/virtual-keys - list all VKs (includes budgets on each VK and each provider config)
  • GET /api/governance/virtual-keys/{id} - get a single VK with full budget details
  • GET /api/governance/virtual-keys/quota - self-service endpoint (authenticate with the VK value via x-bf-vk header)

Breaking Change 7: Provider Keys API Separated

Provider key management now has dedicated endpoints. The keys field has been removed from all provider API requests and responses.

What changed

Before (v1.4.x)After (v1.5.0)
GET /api/providers/{p} returns keyskeys field removed from provider response
POST /api/providers accepts keyskeys field ignored - create keys separately
PUT /api/providers/{p} accepts keyskeys field ignored - update keys via dedicated endpoints

New endpoints

MethodEndpointDescription
GET/api/providers/{provider}/keysList all keys
GET/api/providers/{provider}/keys/{key_id}Get a single key
POST/api/providers/{provider}/keysCreate a key
PUT/api/providers/{provider}/keys/{key_id}Update a key
DELETE/api/providers/{provider}/keys/{key_id}Delete a key

How to update

Creating a provider with keys: Before:
curl -X POST localhost:8080/api/providers -d '{
  "provider": "openai",
  "keys": [{"name": "main", "value": "sk-..."}]
}'
After: Create provider first, then add keys:
curl -X POST localhost:8080/api/providers -d '{"provider": "openai"}'
curl -X POST localhost:8080/api/providers/openai/keys -d '{"name": "main", "value": "sk-..."}'
Reading keys: Before: curl localhost:8080/api/providers/openai | jq '.keys' After: curl localhost:8080/api/providers/openai/keys | jq '.keys' Updating / deleting keys: Before: Bulk replace via provider update:
curl -X PUT localhost:8080/api/providers/openai -d '{"keys": [{"id": "key-1", "value": "sk-new"}]}'
After: Individual key operations:
curl -X PUT localhost:8080/api/providers/openai/keys/key-1 -d '{"name": "updated", "value": "sk-new"}'
curl -X DELETE localhost:8080/api/providers/openai/keys/key-2

Breaking Change 8: Compat Plugin Restructured

The enable_litellm_fallbacks option has been removed and replaced with three granular options. Before:
{ "compat": { "enable_litellm_fallbacks": true } }
After:
{
  "compat": {
    "convert_text_to_chat": true,
    "convert_chat_to_responses": true,
    "should_drop_params": true
  }
}
Old optionNew optionDescription
enable_litellm_fallbacksconvert_text_to_chatText completion → chat completion fallback
(new)convert_chat_to_responsesChat completion → Responses API fallback
(new)should_drop_paramsDrop unsupported OpenAI-compatible params
Response field changes:
FieldChange
extra_fields.litellm_compatRemoved
extra_fields.dropped_compat_plugin_paramsAdded - lists params dropped by this plugin
extra_fields.converted_request_typeAdded - the request type it was converted to

Breaking Change 9: Replicate Image Edits Removed from Generations Endpoint

The /v1/images/generations endpoint on the Replicate provider no longer accepts image editing parameters (source image, mask). It now only handles text-to-image generation. If you were passing image editing parameters to /v1/images/generations on Replicate, switch to /v1/images/edits.
Support for image editing via /v1/images/edits on Replicate is also being removed in a follow-up release. Plan to migrate to an alternative provider.

Breaking Change 10: Provider deployments removed - migrate to aliases

Provider deployment mappings should now live in the top-level aliases field on each key. Aliases work across all providers and map any model name to a provider-specific identifier (deployment name, inference profile ARN, fine-tuned model ID, etc.). The database migration runs automatically on startup, migrating existing deployment data into aliases. Only config.json files need to be updated manually.

Azure

Before:
{
  "providers": {
    "azure": {
      "keys": [{
        "value": "env.AZURE_API_KEY",
        "azure_key_config": {
          "endpoint": "env.AZURE_ENDPOINT",
          "deployments": {
            "gpt-4o": "my-gpt4o-deployment",
            "gpt-4o-mini": "my-mini-deployment"
          }
        }
      }]
    }
  }
}
After:
{
  "providers": {
    "azure": {
      "keys": [{
        "value": "env.AZURE_API_KEY",
        "azure_key_config": {
          "endpoint": "env.AZURE_ENDPOINT"
        },
        "aliases": {
          "gpt-4o": "my-gpt4o-deployment",
          "gpt-4o-mini": "my-mini-deployment"
        }
      }]
    }
  }
}

Bedrock

bedrock_key_config.deployments is a legacy field and is removed in v1.5.0 config semantics. Some setups/builds may still accept it for backward compatibility, but do not rely on it - migrate to aliases to avoid silent breakage and future removal.
Before:
{
  "bedrock_key_config": {
    "region": "env.AWS_REGION",
    "deployments": {
      "claude-3-5-sonnet": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0"
    }
  }
}
After:
{
  "bedrock_key_config": {
    "region": "env.AWS_REGION"
  },
  "aliases": {
    "claude-3-5-sonnet": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0"
  }
}

Vertex

Before:
{
  "vertex_key_config": {
    "project_id": "env.VERTEX_PROJECT_ID",
    "project_number": "env.VERTEX_PROJECT_NUMBER",
    "region": "env.VERTEX_REGION",
    "auth_credentials": "env.VERTEX_AUTH_CREDENTIALS",
    "deployments": {
      "gemini-2.0-flash": "projects/my-project/locations/us-central1/endpoints/123456"
    }
  }
}
After:
{
  "vertex_key_config": {
    "project_id": "env.VERTEX_PROJECT_ID",
    "project_number": "env.VERTEX_PROJECT_NUMBER",
    "region": "env.VERTEX_REGION",
    "auth_credentials": "env.VERTEX_AUTH_CREDENTIALS"
  },
  "aliases": {
    "gemini-2.0-flash": "projects/my-project/locations/us-central1/endpoints/123456"
  }
}

Replicate

The Replicate key config is also restructured. The deployments map is gone. A new boolean use_deployments_endpoint controls whether requests are routed through the Deployments API (private, fixed hardware) or the standard Models API. Before:
{
  "replicate_key_config": {
    "deployments": {
      "my-model": "owner/model-name/version-hash"
    }
  }
}
After:
{
  "replicate_key_config": {
    "use_deployments_endpoint": true
  },
  "aliases": {
    "my-model": "owner/model-name"
  }
}
Old fieldNew fieldNotes
replicate_key_config.deploymentsRemovedUse top-level aliases
(new)replicate_key_config.use_deployments_endpointbool, default false

Breaking Change 11: Go SDK - ExtraFields Model Fields Renamed

ModelRequested string has been replaced by two fields on BifrostResponseExtraFields and BifrostErrorExtraFields. Before:
model := response.ExtraFields.ModelRequested
After:
// The alias the caller passed as "model" in the request
original := response.ExtraFields.OriginalModelRequested

// The actual identifier sent to the provider API
// Equals OriginalModelRequested when no alias is configured
resolved := response.ExtraFields.ResolvedModelUsed
The same rename applies to BifrostErrorExtraFields. JSON tag changes:
OldNew
"model_requested""original_model_requested" + "resolved_model_used"

Breaking Change 12: Go SDK - StreamAccumulatorResult Field Renamed

Model string has been replaced by two fields on StreamAccumulatorResult (returned by tracer streaming accumulation methods). Before:
result.Model
After:
result.RequestedModel // original alias from the caller
result.ResolvedModel  // actual model identifier used by the provider

Breaking Change 13: selected_key_id Cleared on Terminal Retry Failures

With the introduction of multi-key retry rotation, selected_key_id (and selected_key_name) in the request context are cleared when all retry attempts fail. Previously, these fields always reflected the key that was selected for the request, even on error. The attempt_trail is now the authoritative record of every key tried and why each attempt failed.

What changed

FieldBeforeAfter
selected_key_idAlways set, even on errorEmpty string when all retries exhausted
selected_key_nameAlways set, even on errorEmpty string when all retries exhausted
attempt_trailNot presentArray of { attempt, key_id, key_name, fail_reason, triggered_rotation } per attempt. fail_reason is set on every failed attempt; triggered_rotation is always present, set to true only when this attempt’s per-key failure (rate-limit (429), auth (401/403), or billing (402)) caused the next retry to switch keys, false otherwise.

Impact on logging and telemetry plugins

The built-in logging plugin writes selected_key_id and selected_key_name directly to each log record. For multi-key requests that exhaust all retries, both fields will be empty in the stored log entry. The attempt_trail column captures the full per-attempt key history and is the correct field to use for failure attribution. The built-in telemetry plugin emits selected_key_id and selected_key_name as span attributes. For exhausted-retry failures these attributes will be empty strings on the error span. The attempt_trail span attribute contains the full rotation history. If you run a custom plugin or downstream log consumer that filters or groups by selected_key_id to track which key caused a failure, you must update it to handle the empty-string case and read from attempt_trail when attribution is needed.

How to update

If you read selected_key_id from plugin context to attribute failed requests: Before:
keyID, _ := ctx.Value(schemas.BifrostContextKeySelectedKeyID).(string)
// keyID was always populated, even on error
After:
// Populated on success (or for single-key / pinned / sticky flows on error):
keyID, _ := ctx.Value(schemas.BifrostContextKeySelectedKeyID).(string)

// For full attribution across all retry attempts (including failures):
if trail, ok := ctx.Value(schemas.BifrostContextKeyAttemptTrail).([]schemas.KeyAttemptRecord); ok {
    for _, record := range trail {
        // record.KeyID, record.KeyName, record.FailReason
    }
}
If you consume selected_key_id from the logging REST API: The selected_key_id field on a LogEntry may now be an empty string when the request failed after exhausting all retries. Use attempt_trail for the full per-attempt key history.
Single-key, pinned (x-bf-key-id / x-bf-key-name), and session-sticky requests are unaffected - they never rotate keys, so selected_key_id remains populated on failure for those flows.

Breaking Change 14: Direct Key Bypass Removed (HTTP Gateway and Go SDK)

The “Direct Key Bypass” feature has been removed entirely in v1.5.0, on both surfaces:
  • HTTP gateway: the allow_direct_keys config flag and the header pass-through (Authorization, x-api-key, x-goog-api-key, plus the Bedrock x-bf-bedrock-* and Azure x-bf-azure-endpoint integration paths) no longer forward keys to upstream providers.
  • Go SDK: the schemas.BifrostContextKeyDirectKey context value has been deleted, along with the documented “Direct Key (Go SDK Only)” API.
All requests must now resolve to a Bifrost-managed provider key, either implicitly (the configured key pool plus weighted selection) or by pinning a registered key via BifrostContextKeyAPIKeyID / BifrostContextKeyAPIKeyName (Go SDK) or virtual keys (sk-bf-*, HTTP).

What changed

Field / surfaceStatus
client.allow_direct_keys in config.jsonRemoved — field is no longer recognized; ignored if present
client_config.allow_direct_keys over PUT /api/configRemoved — field is dropped from the request payload
Web UI Settings → Security → “Allow Direct API Keys” toggleRemoved
Authorization: Bearer sk-... header → upstream providerNo longer forwarded as a direct key. Bearer values starting with sk-bf- continue to work as virtual keys
x-api-key / x-goog-api-key header → upstream providerNo longer forwarded as a direct key
x-bf-bedrock-api-key / x-bf-bedrock-access-key / x-bf-bedrock-secret-key / x-bf-bedrock-session-token / x-bf-bedrock-regionNo longer extracted by the Bedrock integration
x-bf-azure-endpoint + bare Authorization header on Azure OpenAI integration routesNo longer extracted as a direct Azure key
Database column config_client.allow_direct_keysAuto-dropped on first startup of v1.5.0
schemas.BifrostContextKeyDirectKey (Go SDK constant)Removed — code referencing it will fail to compile
Bifrost.getAllSupportedKeys / getKeysForBatchAndFileOps / selectKeyFromProviderForModelWithPool DirectKey branchesRemoved — these no longer special-case a caller-supplied key

How to update

1. Remove the field from config.json:
{
  "client": {
    "enable_logging": true,
-   "allow_direct_keys": false,
    "allowed_origins": ["*"]
  }
}
The field is silently ignored, but leaving it in is misleading. 2. Remove the field from any REST API integration that calls PUT /api/config: the field has been dropped from the client_config schema. 3. Migrate any HTTP caller that relied on header-passed keys:
  • For per-tenant or per-user key isolation: create a Bifrost virtual key per tenant (with budgets, rate limits, and provider/model allow-lists) and have callers send Authorization: Bearer sk-bf-<tenant-vk>.
  • For routing requests across multiple provider keys: add the keys to Bifrost via POST /api/providers/{provider}/keys and let Bifrost handle weighted selection and rotation.
  • For Bedrock callers using AWS credentials in x-bf-bedrock-* headers: add a Bedrock provider key with the same credentials (access_key, secret_key, region, optional session_token) via the providers API and reference it from your virtual key.
  • For Azure direct-key callers using x-bf-azure-endpoint + Authorization: add the Azure deployment as a provider key with azure_key_config.endpoint and reference it from your virtual key.
4. Migrate Go SDK callers off BifrostContextKeyDirectKey: The constant is gone. Replace any in-process injection with one of the following supported alternatives. Before (no longer compiles):
ctx = context.WithValue(ctx, schemas.BifrostContextKeyDirectKey, schemas.Key{
    Value:  *schemas.NewEnvVar("sk-runtime-secret"),
    Models: []string{"gpt-4o"},
    Weight: 1.0,
})
After — register the key with the account, then pin by ID or name:
// In your schemas.Account implementation, return the key from GetKeysForProvider.
// Each registered key has a stable ID and Name; reference either one from the request context.

ctx = context.WithValue(ctx, schemas.BifrostContextKeyAPIKeyName, "runtime-secret")
// or
ctx = context.WithValue(ctx, schemas.BifrostContextKeyAPIKeyID, "<key-id-from-account>")
If your account is database-backed, add the key via POST /api/providers/{provider}/keys. If you maintain a custom in-memory Account, return the key from GetKeysForProvider. Both routes give you the same per-request pinning behaviour DirectKey provided, plus governance, rotation on retry, and per-key cost attribution. For providers that allow keyless requests (ambient credentials, IAM roles, etc.), BifrostContextKeySkipKeySelection is unchanged.

Why it was removed

The direct-key path bypassed Bifrost’s key management entirely, which meant:
  • No governance — virtual key budgets, rate limits, provider/model allow-lists, and routing rules were not applied to direct-key traffic
  • No rotation or fallback — direct keys were used as-is with no retry across alternate keys
  • No observability attribution — header-provided keys had a synthetic key_id: "header-provided" that defeated per-key cost and usage analytics
  • Security surface area — a misconfigured HTTP deployment could leak provider credentials through logs or proxy chains; the Go SDK equivalent had the same hazard for any caller logging context contents
The supported BifrostContextKeyAPIKeyID / BifrostContextKeyAPIKeyName path covers the “pick a specific key per request” use case without the governance and observability gaps.

Breaking Change 15: Semantic Cache Clear API is Now Cache-ID Based

The semantic cache “clear by request ID” API has been removed. Storage IDs in the cache are deterministic UUIDv5 hashes derived from the request payload (so the same prompt across many requests maps to a single cache entry), which made the previous request-ID-based delete unable to match anything written by the direct-search path. The replacement is keyed on the cache entry’s storage ID, which is now stamped on every response in extra_fields.cache_debug.cache_id — on cache hits and cache misses. Hold onto that ID from the response if you ever need to invalidate the entry.

REST API

Before (v1.4.x)After (v1.5.0)
DELETE /api/cache/clear/{requestId}DELETE /api/cache/clear/{cacheId}
The path parameter name and meaning both changed. The cache key endpoint (DELETE /api/cache/clear-by-key/{cacheKey}) is unchanged. Before:
curl -X DELETE localhost:8080/api/cache/clear/req-aaa-bbb-ccc
After:
# Read the cache ID from a prior response
CACHE_ID=$(curl ... | jq -r '.extra_fields.cache_debug.cache_id')

curl -X DELETE localhost:8080/api/cache/clear/$CACHE_ID

Go SDK

The ClearCacheForRequestID method on *semanticcache.Plugin has been removed and replaced by ClearCacheForCacheID. Before:
err := plugin.ClearCacheForRequestID(requestID)
After:
// On hit or miss, the storage ID is exposed via CacheDebug.CacheID
cacheID := response.ExtraFields.CacheDebug.CacheID
if cacheID != nil {
    err := plugin.ClearCacheForCacheID(*cacheID)
}

Why the rename

A single cache entry is reused across many request IDs (that is the point of caching). A request-ID-based delete only ever made sense for the original writer of the entry, and even that broke once direct search switched to deterministic storage IDs. The cache ID is the only stable handle that works for both writers and readers, so the API now reflects that.

CacheDebug on misses

extra_fields.cache_debug is now populated on cache misses too — previously it was only emitted when semantic search ran. The new fields on a miss:
  • cache_hit: false
  • cache_id: the storage ID where the entry was written (use this with ClearCacheForCacheID)
  • provider_used / model_used / input_tokens: only present when semantic search actually ran (i.e. embedding model was invoked)
If you parse cache_debug and assumed it was either absent or had cache_hit: true, update your consumer to handle the cache_hit: false shape.

Breaking Change 16: Semantic Cache cleanup_on_shutdown Removed

The cleanup_on_shutdown option on the semantic cache plugin config has been removed. Cache entries and the vector store namespace are no longer deleted when Bifrost shuts down — cache data always persists between restarts. Before:
{
  "plugins": {
    "semantic_cache": {
      "config": {
        "ttl": "5m",
        "cleanup_on_shutdown": true
      }
    }
  }
}
After:
{
  "plugins": {
    "semantic_cache": {
      "config": {
        "ttl": "5m"
      }
    }
  }
}
The field is no longer part of the config schema and will be rejected by validation. Remove it from config.json, Helm values, and any PUT /api/config payloads.

How to clear cache data

If you previously relied on cleanup_on_shutdown: true to drop the cache on restart, use one of the supported invalidation paths instead:
  • DELETE /api/cache/clear/{cacheId} — invalidate a single entry
  • DELETE /api/cache/clear-by-key/{cacheKey} — invalidate all entries for a cache key
  • Drop the vector store class/collection or point vector_store_namespace at a fresh name to start clean

Dimension / provider / model changes

The previous cleanup_on_shutdown: true + restart workflow was the documented escape hatch for changing dimension (or switching to an embedding provider/model that produces a different vector size). That option is gone. To rotate the namespace now, either:
  • point vector_store_namespace at a fresh name, or
  • drop the existing class/index in your vector store before restarting

Opting Out: version: 1 Compatibility Mode

If you are not ready to adopt the new deny-by-default semantics, you can add a single field to config.json to restore v1.4.x behavior for all allow-list fields loaded from that file:
{
  "version": 1,
  "providers": { ... }
}
ValueBehavior
2 (default, omitted)v1.5.0 semantics - empty = deny all, ["*"] = allow all
1v1.4.x semantics - empty = allow all
What version: 1 normalizes at startup (before any other processing):
FieldWithout version: 1With version: 1
Provider key models: []Deny all modelsAllow all models (→ ["*"])
VK provider_configs: []No providers allowedAll configured providers added with allowed_models: ["*"]
VK provider config allowed_models: []Deny all modelsAllow all models (→ ["*"])
VK provider config key_ids: []No keys allowedAll keys allowed (→ key_ids: ["*"])
VK mcp_configs: []No MCP tools allowedAll configured MCP clients added with tools_to_execute: ["*"]
version: 1 only applies to configuration loaded from config.json. Virtual Keys created or updated via the REST API always use v1.5.0 semantics regardless of this setting. The automatic database migration that runs on startup is also unaffected.
version: 1 is a temporary compatibility shim. Plan to migrate your config.json to explicit ["*"] wildcards and remove the version field before the next major release.

Complete Migration Checklist

1

Backup your database

Make a copy of your config store database before starting the upgrade.
2

Update provider key models in config.json

Replace "models": [] or missing models fields with "models": ["*"] on every provider key.
3

Add allowed_models and key_ids to every VK provider config

Add "allowed_models": ["*"] and "key_ids": ["*"] to every provider_configs entry (or list specific values). Rename any allowed_keys fields to key_ids.
4

Ensure every VK has at least one provider config

Any Virtual Key with "provider_configs": [] or no provider_configs will block all traffic.
5

Update tools_to_execute for MCP configs

Replace "tools_to_execute": [] with "tools_to_execute": ["*"]. Ensure every VK that needs MCP access has at least one mcp_configs entry.
6

Migrate budget to budgets on Virtual Keys

Update any API integration that creates or updates Virtual Keys or provider configs to use the budgets array instead of the singular budget object. Existing budgets are migrated automatically on startup.
7

Handle nullable weight in API consumers

Update any client code that processes weight to accept null in addition to numbers.
8

Fix invalid WhiteList values

Ensure no list mixes "*" with specific values (e.g., ["*", "gpt-4o"]) and no list has duplicate entries.
9

Migrate key management to dedicated endpoints

Stop sending keys in provider create/update payloads and stop reading keys from provider responses. Use /api/providers/{provider}/keys for all key operations.
10

Update compat plugin config

Replace enable_litellm_fallbacks with the appropriate combination of convert_text_to_chat, convert_chat_to_responses, and should_drop_params.
11

Migrate provider deployments to aliases

Move deployment mappings from provider-specific deployments fields into the top-level aliases field on each key. For Replicate, set use_deployments_endpoint: true if you were using the deployments endpoint.
12

Update Go SDK references to ExtraFields.ModelRequested

Replace ExtraFields.ModelRequested with ExtraFields.OriginalModelRequested (and optionally read ExtraFields.ResolvedModelUsed). Update JSON consumers reading "model_requested" to use "original_model_requested" and "resolved_model_used".
13

Update Go SDK references to StreamAccumulatorResult.Model

Replace .Model with .RequestedModel (and optionally .ResolvedModel) on any StreamAccumulatorResult usage.
14

Handle empty selected_key_id on terminal retry failures

If your code reads selected_key_id / selected_key_name from the request context or log entries to attribute failed requests, add a null/empty check and fall back to attempt_trail for the full per-attempt key history.
15

Migrate direct-key callers off both surfaces

Remove allow_direct_keys from config.json and any PUT /api/config payloads. Audit HTTP callers that sent provider keys in Authorization / x-api-key / x-goog-api-key / x-bf-bedrock-* / x-bf-azure-endpoint headers — those keys are no longer forwarded. Audit Go SDK callers for any reference to schemas.BifrostContextKeyDirectKey — the constant is removed and code referencing it will not compile. Replace both flavours with a Bifrost-managed provider key, optionally pinned per request via BifrostContextKeyAPIKeyID / BifrostContextKeyAPIKeyName (Go SDK) or a virtual key (sk-bf-*, HTTP).
16

Switch semantic cache invalidation to cache IDs

Replace DELETE /api/cache/clear/{requestId} with DELETE /api/cache/clear/{cacheId}, and replace plugin.ClearCacheForRequestID(...) with plugin.ClearCacheForCacheID(...). Read the cache ID from extra_fields.cache_debug.cache_id on the response (now populated on misses too).
17

Remove cleanup_on_shutdown from semantic cache config

Drop the cleanup_on_shutdown field from the semantic cache plugin config in config.json, Helm values, and any API payloads — it is no longer part of the schema. Cache data now always persists across restarts; use the cache clear endpoints or rotate vector_store_namespace to drop entries.

Troubleshooting

All requests returning 403/blocked after upgrade A provider key has models: [], a Virtual Key has no provider_configs, or a provider config has allowed_models: []. Check Bifrost logs - a blocked request logs which rule denied it. Fix: add "models": ["*"] on provider keys, "allowed_models": ["*"] on VK provider configs. MCP tools not being injected / tool calls blocked The VK needs an mcp_configs entry for the MCP client with "tools_to_execute": ["*"] (or specific tools). API returning 400 on VK create/update A whitelist validation failure - either mixing "*" with specific values, or duplicate values in a list. “No keys available” or key selection errors A provider config with key_ids omitted or [] now blocks all keys (allow_all_keys: false). Add "key_ids": ["*"]. Provider create/update errors about keys field The keys field has been removed. Remove it from provider payloads and use /api/providers/{provider}/keys instead. Replicate requests failing after upgrade If you used replicate_key_config.deployments, move the mappings to the top-level aliases field and set use_deployments_endpoint: true if you were targeting the Deployments API. Go SDK compilation errors on ModelRequested or StreamAccumulatorResult.Model Rename to OriginalModelRequested/ResolvedModelUsed on ExtraFields, and RequestedModel/ResolvedModel on StreamAccumulatorResult. Calls authenticating with raw provider keys started failing with auth errors The HTTP gateway no longer extracts provider keys from Authorization / x-api-key / x-goog-api-key headers (or x-bf-bedrock-* / x-bf-azure-endpoint on the Bedrock and Azure integrations). Either issue a virtual key (sk-bf-*) per caller and have them send that, or register the provider credentials as a Bifrost-managed key and route via virtual keys. See Breaking Change 14. Virtual Key response missing budgets field If you are creating Virtual Keys via the API using the old singular budget field, it will be ignored. Use the new budgets array format instead. If the VK was migrated from v1.4.x, the automatic migration should have converted the existing budget. Check that the migration ran successfully on startup. Go SDK build error: undefined: schemas.BifrostContextKeyDirectKey The constant is removed in v1.5.0. Register the key on your Account implementation (or via POST /api/providers/{provider}/keys if you use the database-backed config store) and pin it per request with BifrostContextKeyAPIKeyID or BifrostContextKeyAPIKeyName. See Breaking Change 14 for a before/after example.