Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docker-volume/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"config_store": {
"enabled": true,
"type": "sqlite",
"config": {
"path": "/home/vedran/tmp/bifrost/docker-volume/config.db"
}
},
"logs_store": {
"enabled": true,
"type": "sqlite",
"config": {
"path": "/home/vedran/tmp/bifrost/docker-volume/logs.db"
}
},
"plugins": [
{
"enabled": true,
"name": "bifrost-userinfo-plugin",
"path": "/home/vedran/work/r_and_d/jarvis/bifrost-userinfo-plugin/build/bifrost-userinfo-plugin-local.so",
"version": 1,
"config": {
"vk_name_header": "x-user-info"
}
}
]
}
4 changes: 4 additions & 0 deletions docs/changelogs/helm-v2.1.32.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ description: "Helm v2.1.32 changelog - 2026-07-24"

<Update label="Bifrost Helm" description="v2.1.32">

<Warning>
**Known issue - use v2.1.33 instead.** Multi-profile OTEL configs (`bifrost.plugins.otel.config.profiles`) fail Helm schema validation on this version (`Additional property export_timeout is not allowed`), blocking render and deploy. Fixed in v2.1.33.
</Warning>

## Changelog

- Extended `bifrost.accessProfiles[].provider_configs[]` with `blacklisted_models` (denylist that wins over `allowed_models`; `["*"]` blocks every model, while an empty or omitted list blocks none), `weight` (load-balancer seed weight; `null` opts out), and `model_budgets[]` (per-model budget groups; each entry requires `model_name` and may carry optional `budgets[]` and a `rate_limit`). These pass through into `access_profiles[].provider_configs[]`.
Expand Down
12 changes: 12 additions & 0 deletions docs/changelogs/helm-v2.1.33.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "v2.1.33"
description: "Helm v2.1.33 changelog - 2026-07-31"
---

<Update label="Bifrost Helm" description="v2.1.33">

## Changelog

- Fixed Helm schema validation failure for multi-profile OTEL configs (`bifrost.plugins.otel.config.profiles`), introduced by the `export_timeout` default in 2.1.32.

</Update>
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,7 @@
"item": "Helm",
"icon": "box",
"pages": [
"changelogs/helm-v2.1.33",
"changelogs/helm-v2.1.32",
"changelogs/helm-v2.1.31",
"changelogs/helm-v2.1.30",
Expand Down
10 changes: 10 additions & 0 deletions docs/features/governance/budget-and-limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ By default, a budget **rolls**: after `reset_duration` elapses since `last_reset

Calendar alignment applies to budgets on **customers**, **teams**, **virtual keys**, and **per–provider-config** budgets. You can set it when creating a budget (`calendar_aligned` on create) or toggle it on update (`calendar_aligned` on the budget in `PUT` requests). Turning calendar alignment **on** for an existing budget resets **current usage to zero** and snaps **`last_reset`** to the current period start.

### Budget overrides

A budget can carry a temporary **override** that adds spending capacity on top of its configured limit without touching the base limit, current usage, or reset schedule. While an override is active, enforcement uses:

```text
Effective limit = max_limit + override_amount
```

An override lasts either for a fixed number of reset cycles (the current cycle counts as the first) or until it is explicitly removed. Manage it from the virtual key's **Budget Information** panel, or through `PUT`/`DELETE` on `/api/governance/virtual-keys/{vk_id}/budgets/{budget_id}/override` — see [Budget Overrides](./virtual-keys#budget-overrides) for the UI walkthrough and API examples.

---

## Customer-scoped requests
Expand Down
59 changes: 58 additions & 1 deletion docs/features/governance/virtual-keys.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -516,12 +516,69 @@ For example, adding a `$100` override to a `$1,000` budget raises its effective

![Virtual-key budget with an active override](../../media/vk-override-budget-overriden-budget-view.png)

To change or remove an active override, click **Edit override** on the budget card. For programmatic configuration, see the **Virtual Keys** section of the [API Reference](/api-reference).
To change or remove an active override, click **Edit override** on the budget card.

<Note>
Overrides are available after a budget has been created. Budgets inherited from an enterprise [access profile](/enterprise/access-profiles) must be overridden from that access profile instead of from the virtual key.
</Note>

#### Managing overrides via API

Two endpoints manage the override on a single virtual-key budget. Both take the virtual key ID and the ID of a budget that key owns, and both return the persisted budget alongside its `effective_max_limit`.

Grant extra spend for a fixed number of reset cycles:

```bash
curl -X PUT http://localhost:8080/api/governance/virtual-keys/{vk_id}/budgets/{budget_id}/override \
-H "Content-Type: application/json" \
-d '{
"amount": 100.0,
"mode": "cycles",
"cycles": 2
}'
```

The current cycle counts as the first, so `"cycles": 2` covers the rest of this window plus the next one. Use `"mode": "forever"` (with no `cycles`) to keep the override active until it is removed:

```bash
curl -X PUT http://localhost:8080/api/governance/virtual-keys/{vk_id}/budgets/{budget_id}/override \
-H "Content-Type: application/json" \
-d '{
"amount": 100.0,
"mode": "forever"
}'
```

```json Response
{
"budget": {
"id": "budget-vk-alpha",
"max_limit": 1000.0,
"reset_duration": "1M",
"current_usage": 240.5,
"override_amount": 100.0,
"override_mode": "forever",
"last_reset": "2025-01-01T00:00:00Z"
},
"effective_max_limit": 1100.0
}
```

A PUT always replaces the existing override rather than adding to it, so re-sending an override with a new amount or mode is the way to change one. Remove it with a DELETE, which restores enforcement against the base `max_limit`:

```bash
curl -X DELETE http://localhost:8080/api/governance/virtual-keys/{vk_id}/budgets/{budget_id}/override
```

Notes on behavior:

- The override never changes `max_limit`, `current_usage`, or the reset schedule — only the limit that usage is enforced against.
- `amount` must be greater than 0. In `cycles` mode, `cycles` must be greater than 0; in `forever` mode it must be omitted.
- A finite grant is anchored to the budget's current reset window, so remaining cycles are derived from that grant on every reset. Each node in a cluster computes the same count, and a config reload cannot hand back a cycle that was already spent.
- A cycles override clears itself once every granted window has closed; `DELETE` clears one at any time and cannot be undone.

For the full request and response schema, see [Set virtual key budget override](/api-reference/governance/set-virtual-key-budget-override) and [Remove virtual key budget override](/api-reference/governance/remove-virtual-key-budget-override) in the API Reference.

### Making Virtual Keys Mandatory

All governance-enabled requests must include the virtual key header:
Expand Down
221 changes: 221 additions & 0 deletions docs/openapi/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -45032,6 +45032,158 @@
}
}
},
"/api/governance/virtual-keys/{vk_id}/budgets/{budget_id}/override": {
"put": {
"operationId": "updateVirtualKeyBudgetOverride",
"summary": "Set virtual key budget override",
"description": "Sets or replaces the spending override on one of a virtual key's budgets. The override is additive —\nwhile it is active the budget is enforced against `max_limit + override_amount` — and it leaves the\nbudget's base limit, current usage, and reset schedule untouched.\n\nUse `mode: cycles` with a `cycles` count to grant extra spend for a finite number of reset windows\n(the current window counts as the first), or `mode: forever` to keep the override until it is deleted.\nA finite grant is anchored to the budget's current reset window, so every node in a cluster derives the\nsame number of remaining cycles and a config reload cannot resurrect a spent one.\n",
"tags": [
"Governance"
],
"parameters": [
{
"name": "vk_id",
"in": "path",
"required": true,
"description": "Virtual key ID",
"schema": {
"type": "string"
}
},
{
"name": "budget_id",
"in": "path",
"required": true,
"description": "ID of a budget owned by the virtual key",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BudgetOverrideRequest"
}
}
}
},
"security": [
{
"ManagementBearerAuth": []
}
],
"responses": {
"200": {
"description": "Budget override applied successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BudgetOverrideResponse"
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BifrostError"
}
}
}
},
"404": {
"description": "Virtual key or budget not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BifrostError"
}
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BifrostError"
}
}
}
}
}
},
"delete": {
"operationId": "deleteVirtualKeyBudgetOverride",
"summary": "Remove virtual key budget override",
"description": "Removes any active override from the budget, so it is enforced against its base `max_limit` again.\nThe budget's current usage and reset schedule are unchanged, and the removal is permanent — a cleared\ngrant cannot be re-derived. Safe to call on a budget that has no override.\n",
"tags": [
"Governance"
],
"parameters": [
{
"name": "vk_id",
"in": "path",
"required": true,
"description": "Virtual key ID",
"schema": {
"type": "string"
}
},
{
"name": "budget_id",
"in": "path",
"required": true,
"description": "ID of a budget owned by the virtual key",
"schema": {
"type": "string"
}
}
],
"security": [
{
"ManagementBearerAuth": []
}
],
"responses": {
"200": {
"description": "Budget override removed successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BudgetOverrideResponse"
}
}
}
},
"404": {
"description": "Virtual key or budget not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BifrostError"
}
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BifrostError"
}
}
}
}
}
}
},
"/api/governance/teams": {
"get": {
"operationId": "listTeams",
Expand Down Expand Up @@ -82134,6 +82286,31 @@
"current_usage": {
"type": "number"
},
"override_amount": {
"type": "number",
"description": "Additional spend added on top of `max_limit` while the override is active. Omitted when no override is set."
},
"override_mode": {
"type": "string",
"description": "How long the override stays active. `cycles` keeps it active for a finite number of\nreset windows; `forever` keeps it active until it is removed. Omitted when no override is set.\n",
"enum": [
"cycles",
"forever"
]
},
"override_cycles_remaining": {
"type": "integer",
"description": "Reset windows the override is still valid for, including the current one. Positive only\nfor `cycles` mode; recomputed from the original grant on every reset, never decremented in place.\n"
},
"override_cycles_total": {
"type": "integer",
"description": "Number of reset windows the current `cycles` override was granted for. Immutable for the life of the grant."
},
"override_anchor_reset": {
"type": "string",
"format": "date-time",
"description": "Reset-window boundary at which the current `cycles` override was granted. Immutable for the life of the grant."
},
"team_id": {
"type": "string",
"description": "Team that owns this budget, when the budget is team-scoped"
Expand Down Expand Up @@ -82301,6 +82478,50 @@
}
}
},
"BudgetOverrideRequest": {
"type": "object",
"description": "Replaces the active override on one budget. The override is additive: it does not change the\nbudget's `max_limit`, `current_usage`, or reset schedule. Sending this request against a budget\nthat already has an override replaces that override entirely.\n",
"required": [
"amount",
"mode"
],
"properties": {
"amount": {
"type": "number",
"description": "Additional spend in dollars added on top of `max_limit` while the override is active. Must be greater than 0 and finite."
},
"mode": {
"type": "string",
"description": "`cycles` keeps the override active for `cycles` reset windows starting with the current one.\n`forever` keeps it active until it is removed with a DELETE.\n",
"enum": [
"cycles",
"forever"
]
},
"cycles": {
"type": "integer",
"description": "Number of reset windows the override stays active for, counting the current window as the first.\nRequired and must be greater than 0 when `mode` is `cycles`; must be omitted or 0 when `mode` is `forever`.\n",
"minimum": 1
}
}
},
"BudgetOverrideResponse": {
"type": "object",
"description": "The persisted budget and the additive limit now in force.",
"required": [
"budget",
"effective_max_limit"
],
"properties": {
"budget": {
"$ref": "#/components/schemas/Budget"
},
"effective_max_limit": {
"type": "number",
"description": "`max_limit` plus `override_amount` while an override is active; equal to `max_limit` otherwise."
}
}
},
"CreateRateLimitRequest": {
"type": "object",
"description": "Create rate limit request",
Expand Down
Loading