Skip to content

fix: fixes create virtual key double usage counting - #4070

Merged
akshaydeo merged 1 commit into
devfrom
06-05-fix_fixes_create_virtual_key_double_usage_counting
Jun 5, 2026
Merged

fix: fixes create virtual key double usage counting#4070
akshaydeo merged 1 commit into
devfrom
06-05-fix_fixes_create_virtual_key_double_usage_counting

Conversation

@roroghost17

@roroghost17 roroghost17 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a double-counting bug on newly created virtual keys where the in-memory store retained a direct pointer to the caller's TableVirtualKey. After CreateVirtualKeyInMemory returned, hydrateVKGovernance would mutate that same pointer — injecting model-config-owned rate-limit and budget identities onto the VK's fields — causing the usage tracker to count each request twice (once via the VK-scoped model path and again via the VK-hierarchy path).

Changes

  • CreateVirtualKeyInMemory now works on a shallow clone of the incoming *TableVirtualKey before storing it, ensuring post-create mutations by the caller do not affect the tracked VK's hierarchy fields.
  • Added a regression test (TestGovernanceStore_CreateVirtualKeyInMemory_DecouplesFromCallerPointer) that simulates the hydrateVKGovernance mutation pattern and asserts the stored VK remains unaffected.

Type of change

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

Affected areas

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

How to test

go test ./plugins/governance/... -run TestGovernanceStore_CreateVirtualKeyInMemory_DecouplesFromCallerPointer -v
go test ./plugins/governance/...

The new regression test will fail on the previous code and pass with the clone fix in place. Verify no other virtual key store tests regress.

Breaking changes

  • Yes
  • No

Related issues

Regression introduced by the hydrateVKGovernance post-store mutation pattern on newly created virtual keys.

Security considerations

No auth, secrets, or PII implications. The fix prevents incorrect usage accounting that could allow rate limits and budgets to be bypassed or incorrectly enforced on new virtual keys.

Checklist

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

Summary by CodeRabbit

  • Bug Fixes

    • Stored isolated copies of virtual keys, teams, and customers so external mutations no longer affect tracked governance records; calendar-aligned flags are correctly applied to associated budgets and rate limits.
  • Tests

    • Added a regression test ensuring in-memory governance records remain decoupled from caller-provided data after creation.

@CLAassistant

CLAassistant commented Jun 4, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

CreateVirtualKeyInMemory, CreateTeamInMemory, and CreateCustomerInMemory now clone inputs, stamp IsCalendarAligned on cloned budgets/rate-limits (including provider-config entries), persist cloned entries into flat caches, and store pointers to the clones; a regression test verifies virtual key cloning decouples from the caller's pointer. Two test literals were reformatted.

Changes

In-memory cloning for governance entities

Layer / File(s) Summary
VirtualKey clone and store
plugins/governance/store.go
CreateVirtualKeyInMemory clones the incoming TableVirtualKey, stamps IsCalendarAligned on the cloned budgets and rate-limits (including provider-config), writes related entries keyed by IDs into in-memory maps, and stores &clone in gs.virtualKeys.
Team clone and store
plugins/governance/store.go
CreateTeamInMemory clones the incoming team, stamps IsCalendarAligned on cloned team budgets and attached rate-limit, writes those entries keyed by IDs into in-memory maps, and stores &clone in gs.teams.
Customer clone and store
plugins/governance/store.go
CreateCustomerInMemory clones the incoming customer, stamps IsCalendarAligned on cloned customer budgets and attached rate-limit, writes those entries keyed by IDs into in-memory maps, and stores &clone in gs.customers.
Regression test + test formatting
plugins/governance/store_test.go
Adds TestGovernanceStore_CreateVirtualKeyInMemory_DecouplesFromCallerPointer to assert the stored VK does not reflect post-call mutations to the caller's VK; two TableRateLimit literals reformatted for alignment only.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • maximhq/bifrost#3452: Related governance changes around calendar-aligned state handling for VK-owned/provider-config budgets and rate limits.

Suggested reviewers

  • Pratham-Mishra04

Poem

I'm a rabbit who copies with care, 🐇
I clone your keys and budgets fair.
No pointer leaks, no counts askew,
The store keeps snapshots safe and true.
Hop, test, and ship — a tidy view.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: fixes create virtual key double usage counting' accurately describes the main bug fix—preventing double-counting of virtual key usage by storing a clone instead of a caller pointer.
Description check ✅ Passed The PR description comprehensively covers the template sections: Summary explains the bug, Changes detail the fix and regression test, Type/Areas/Testing are checked, no breaking changes, and security implications are documented.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-05-fix_fixes_create_virtual_key_double_usage_counting

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

Copy link
Copy Markdown
Contributor Author

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

@roroghost17
roroghost17 marked this pull request as ready for review June 4, 2026 22:13
@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The change is safe to merge: it decouples stored governance records from caller-owned pointers without altering any public interfaces or update/delete paths.

All three Create*InMemory functions now store clones rather than the caller's pointer. The critical ProviderConfigs slice receives a proper element-level copy (the exact vector for the double-counting bug). The top-level Budgets slice is not deep-copied, but the actual post-store mutation in applyVKGovernanceFromModelConfigs is a field reassignment (vk.Budgets = mc.Budgets), not an in-place element mutation, so the shared backing array is never written through. The regression test directly exercises the mutation pattern described in the PR and all existing governance store tests remain intact.

No files require special attention.

Important Files Changed

Filename Overview
plugins/governance/store.go CreateVirtualKeyInMemory, CreateTeamInMemory, and CreateCustomerInMemory now work on shallow clones of the incoming pointers; ProviderConfigs receives a proper element-level copy to prevent the applyVKGovernanceFromModelConfigs in-place mutation from escaping into the stored VK's hierarchy fields.
plugins/governance/store_test.go Adds a regression test that simulates the hydrateVKGovernance mutation pattern (both top-level reassignments and in-place ProviderConfigs element mutations) and asserts the stored VK clone remains decoupled.

Reviews (3): Last reviewed commit: "fix: fixes create virtual key double usa..." | Re-trigger Greptile

Comment thread plugins/governance/store.go

Copy link
Copy Markdown
Contributor Author

@coderabbitai full-review

@roroghost17
roroghost17 force-pushed the 06-05-fix_fixes_create_virtual_key_double_usage_counting branch from 94488e1 to 73d7e7e Compare June 4, 2026 22:21
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the diff for PR `#4070`
gh pr diff 4070 --repo maximhq/bifrost

echo "---FILE_CONTENT---"
cat plugins/governance/store.go

echo "---TEST_FILE---"
cat plugins/governance/store_test.go

Length of output: 213295



@roroghost17: Here is the full review of PR #4070.


Review of fix: fixes create virtual key double usage counting

Summary

The root cause is correctly identified and the fix is sound for the described bug pattern. CreateVirtualKeyInMemory (and by extension CreateTeamInMemory / CreateCustomerInMemory) used to store the caller's pointer directly into gs.virtualKeys. After the call returned, hydrateVKGovernance reassigned vk.RateLimit, vk.RateLimitID, and vk.Budgets on the same pointer, causing the stored VK to inherit model-config-owned IDs and produce double-counting on every subsequent request.

The shallow-clone fix (clone := *vk) decouples the stored struct from the caller's pointer so post-create slice/pointer reassignments are invisible to the store.


Correctness of the shallow clone

The three mutation types described for hydrateVKGovernance are all pointer/slice-header reassignments:

vk.RateLimit = hydratedRL       // replaces pointer in caller's struct
vk.RateLimitID = &hydratedRL.ID // replaces pointer in caller's struct
vk.Budgets = []...{...}         // replaces slice header in caller's struct

A shallow copy (clone := *vk) gives clone its own copy of these fields. Reassigning them on vk after the call does not touch clone. The fix is correct for all three cases.

One nuance worth noting: the shallow copy does not protect against in-place mutations through a non-nil shared pointer. Specifically, if vk.RateLimit != nil at call time, clone.RateLimit aliases the same TableRateLimit struct. If the caller subsequently mutates fields of that struct (e.g. vk.RateLimit.ID = "new-id") rather than reassigning the pointer, the stored clone's rate limit is also affected. The test covers only the nil initial RateLimit path (matching the actual hydrateVKGovernance scenario), so this is not a current bug, but it is a latent vulnerability if the caller contract changes.

Recommendation (non-blocking): Consider adding a comment on the shallow clone line explaining its scope:

// Shallow clone breaks the caller's pointer identity so post-create
// pointer/slice reassignments (e.g. hydrateVKGovernance) do not mutate the
// stored entry. Note: pointer fields (RateLimit) still alias the caller's
// struct if non-nil at create time; only reassignments are decoupled.
clone := *vk

Test coverage

The regression test TestGovernanceStore_CreateVirtualKeyInMemory_DecouplesFromCallerPointer is well-structured and correctly exercises the exact mutation pattern from hydrateVKGovernance.

Minor gap (non-blocking): CreateTeamInMemory and CreateCustomerInMemory receive the same clone-before-store treatment but have no equivalent regression tests. If a similar post-create mutation pattern exists for those callers, the coverage gap could hide regressions. A brief parallel test for each would round out confidence.


Logic review of CreateTeamInMemory

The CreateTeamInMemory change is functionally equivalent to the pre-fix code for the budget loop — budgets are still value-copied via b := clone.Budgets[i] before storing &b, so each budget gets its own stack copy. No behavioural change here.


Cosmetic changes

The reformatting of TableRateLimit struct literals in two existing tests (TestGovernanceStore_Customer_CalendarAligned_CreateInMemory and TestGovernanceStore_Customer_CalendarAligned_UpdateInMemory) is cosmetic alignment only — no functional change.


Overall verdict

The fix is correct and addresses the reported bug. The regression test will fail on the old code and pass with the fix. The application of the same pattern to CreateTeamInMemory and CreateCustomerInMemory is good defensive consistency. The two non-blocking suggestions above are optional improvements.


🧠 Learnings used
Learnt from: roroghost17
Repo: maximhq/bifrost PR: 3998
File: framework/configstore/migrations.go:9574-9591
Timestamp: 2026-06-02T16:47:23.453Z
Learning: In maximhq/bifrost reviews, full-table scans in one-time boot migrations should generally be treated as non-blocking optimizations unless there is a clear scale, locking, memory, or correctness risk. Do not escalate such scans as major issues solely because they are broader than necessary; note them as optional improvements when appropriate.

Learnt from: CR
Repo: maximhq/bifrost PR: 0
File: .greptile/rules.md:0-0
Timestamp: 2026-06-03T21:47:30.819Z
Learning: Applies to plugins/governance/**/*.go : Review budget, rate-limit, virtual key, and RBAC paths for fail-closed behavior where security is involved.

Learnt from: CR
Repo: maximhq/bifrost PR: 0
File: .greptile/rules.md:0-0
Timestamp: 2026-06-03T21:47:30.819Z
Learning: Applies to plugins/governance/**/*.go : Check that fallback and retry attempts do not double-count or undercount usage unexpectedly.

Learnt from: roroghost17
Repo: maximhq/bifrost PR: 3998
File: framework/configstore/migrations.go:9538-9562
Timestamp: 2026-06-02T16:35:46.964Z
Learning: In maximhq/bifrost, for customer budget migration logic in framework/configstore/migrations.go (migrationAddCustomerBudgetsToBudgetsTable), duplicate legacy governance_customers.budget_id references are not expected through supported API flows because each customer creates its own budget. Such duplicates would only arise from manual database edits or invalid config.json entries where multiple customers reference the same pre-declared budget ID, so do not treat a missing duplicate-budget_id preflight as a realistic migration blocker unless the PR explicitly supports or validates those unsupported data shapes.

Learnt from: BearTS
Repo: maximhq/bifrost PR: 4043
File: transports/bifrost-http/server/server.go:0-0
Timestamp: 2026-06-04T16:19:42.764Z
Learning: In `transports/bifrost-http/server/server.go` (`ReloadVirtualKey`), the VK-scoped model config stale-eviction logic (snapshot in-memory IDs → DB fetch → delete absent IDs) is intentionally best-effort and not protected by a per-VK lock. Concurrent `ReloadVirtualKey` calls can race and temporarily leave or delete in-memory entries incorrectly, but correctness is guaranteed by convergence: the next successful reload re-fetches from DB and restores the correct state. Do not flag this as a race requiring a lock or stricter ordering; the project explicitly rejects adding a VK-scoped lock for this path.

Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 3934
File: plugins/modelcatalogresolver/main.go:84-118
Timestamp: 2026-06-04T20:54:37.494Z
Learning: In maximhq/bifrost, the "do not set Bifrost reserved context keys from handlers or plugins" rule (learning 3d4b3152) is scoped to `transports/**/*.go`. Plugins in `plugins/**` (including `plugins/modelcatalogresolver/main.go`) are permitted to call `ctx.AppendRoutingEngineLog(...)` and `schemas.AppendToContextList(ctx, schemas.BifrostContextKeyRoutingEnginesUsed, ...)` as part of normal routing-metadata emission. Do not flag these calls in plugin code as violating the reserved-key rule.
✅ Action performed

Full review finished.

@roroghost17
roroghost17 force-pushed the 06-05-fix_fixes_create_virtual_key_double_usage_counting branch from 73d7e7e to a460f35 Compare June 4, 2026 23:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/governance/store.go`:
- Around line 2897-2912: CreateTeamInMemory currently shallow-copies the team
causing caller-owned nested state to be shared: ensure nested mutable fields are
deep-copied before storing them so later mutations by the caller don't affect
in-memory data; specifically, in CreateTeamInMemory make a deep copy of
clone.Budgets (new slice and new Budget structs) and of clone.RateLimit (new
RateLimit struct) before mutating IsCalendarAligned and calling
gs.budgets.Store(b.ID, &b) and gs.rateLimits.Store(clone.RateLimit.ID,
clone.RateLimit), then store the fully independent clone via
gs.teams.Store(clone.ID, &clone) so gs.CheckTeamBudget and CheckTeamRateLimit
read immutable in-memory copies.
- Around line 3031-3040: CreateCustomerInMemory currently does a shallow copy
(clone := *customer) which can alias the caller's nested Budgets slice and
RateLimit pointer; instead deep-copy the nested structures before storing so
stored pointers reference newly allocated copies. Specifically, allocate a new
slice for Budgets, copy each budget element into the new slice, set
IsCalendarAligned on each new element and store pointers to those new elements
into gs.budgets; if clone.RateLimit != nil, allocate a new RateLimit copy, set
its IsCalendarAligned and store that into gs.rateLimits; finally store a pointer
to the new top-level customer struct (which contains the new Budgets slice and
RateLimit pointer) into gs.customers. Ensure all references saved to gs.budgets,
gs.rateLimits and gs.customers point to newly allocated objects to avoid
aliasing the input.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 20b9bab5-431c-4dbc-a52a-4e137511465b

📥 Commits

Reviewing files that changed from the base of the PR and between 73d7e7e and a460f35.

📒 Files selected for processing (2)
  • plugins/governance/store.go
  • plugins/governance/store_test.go

Comment thread plugins/governance/store.go
Comment thread plugins/governance/store.go

Copy link
Copy Markdown
Contributor

Merge activity

  • Jun 5, 12:15 PM UTC: A user started a stack merge that includes this pull request via Graphite.

@akshaydeo
akshaydeo merged commit 1afd0ad into dev Jun 5, 2026
15 of 16 checks passed
@akshaydeo
akshaydeo deleted the 06-05-fix_fixes_create_virtual_key_double_usage_counting branch June 5, 2026 12:15
@akshaydeo akshaydeo mentioned this pull request Jun 5, 2026
18 tasks
akshaydeo added a commit that referenced this pull request Jun 6, 2026
## Summary

This PR bumps the Go toolchain version from `1.26.3` to `1.26.4` across all modules and CI workflows, and cuts a new release (`core` v1.5.17, `framework` v1.3.17, `transports` v1.5.9, `plugins/compat` v0.1.16, `plugins/governance` v1.5.17, and associated plugin versions) incorporating a large batch of features and fixes accumulated since the previous release.

## Changes

- **Go 1.26.4** — Updated `go-version` in all GitHub Actions workflows (`e2e-tests`, `helm-release`, `pr-tests`, `release-cli`, `release-pipeline`, `snyk`) and all `go.mod` files (core, framework, transports, cli, all plugins, examples, and test modules).
- **Core (v1.5.17)** — OpenAI compaction support, multi-customer logs and usage tracking, multiple team/business unit support, `request_headers` wildcard pattern capture for OTel and Maxim plugins, xAI `x_search` tool, fetch URL validation with SSRF hardening, `file://` pricing URL scheme, virtual key provider fan-out filtering, and a broad set of fixes including Anthropic prompt cache key, empty thinking block stripping, OpenAI stream usage event cleanup, Gemini numeric schema constraints, stale connection retries, Azure Claude diagnostic strip, and passthrough budget handling.
- **Framework (v1.3.17)** — Scope-aware budgets and limits wired from model configs, provider-level governance, multiple customer budget support with `calendar_aligned` windows, paginated virtual key fetch, `config.json` source-of-truth flow, FTS index cap reduction, sync worker drift fix, cascade deletes for model configs, and high-scale virtual key flow improvements.
- **Transports (v1.5.9)** — Full changelog covering all of the above plus UI improvements (log navigation, customer detail sheet, `BudgetDisplay` component, inline loading shell, materialized view alias), SCIM provisioning fields, Helm/config schema additions (`roles`, `per_user_oauth`), client IP resolution from forwarded headers, and dependency upgrades (`recharts` to 3.8.1, `golang.org/x` CVE remediation).
- **Plugins** — `governance` v1.5.17 adds team budget/rate-limit exporters, ghost node reconciliation fix, and VK double usage counting fix; `logging` v1.5.17 adds wildcard header capture and file attachment rendering; `otel` v1.2.17 adds `disable_content_logging` and multiple collectors support; `maxim` v1.6.17 adds `request_headers` wildcard capture; `compat` v0.1.16 fixes `max_tokens` preservation during param filtering.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Verify Go version
go version  # should report go1.26.4

# Run core tests
cd core && go test ./...

# Run framework tests
cd framework && go test ./...

# Run transports tests
cd transports && go test ./...

# Run plugin tests
cd plugins/governance && go test ./...
cd plugins/logging && go test ./...
cd plugins/otel && go test ./...

# UI
cd ui
pnpm i
pnpm build
pnpm test
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

#4053, #4066, #4041, #4012, #3976, #3947, #3991, #4045, #3957, #3938, #3937, #3939, #3981, #3998, #3997, #4092, #4091, #4079, #4080, #4086, #3929, #3994, #4028, #3970, #3919, #3861, #3664, #3999, #4088, #4070, #4051, #4043, #4057, #4023, #3941, #3955, #4024, #3956, #3967, #3925, #3992, #3900

## Security considerations

- Fetch URL validation hardened against SSRF by tightening IP checks for private networks and link-local addresses (#4092, #3947, #3991).
- Transitive `golang.org/x` dependencies (crypto, net, sys, text) bumped to address Docker Scout CVEs (#3900).

## Checklist

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * OpenAI compaction, multi-customer/team logstore support, request-header wildcard capture, enhanced governance (provider-level & scope-aware limits), disable-content-logging option, support for multiple OpenTelemetry collectors, SSRF hardening and URL validation.

* **Chores**
  * Bumped Go toolchain across modules and updated component/plugin version releases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@akshaydeo akshaydeo mentioned this pull request Jun 7, 2026
akshaydeo added a commit that referenced this pull request Jun 7, 2026
## ✨ Features

- **OpenAI Compaction** — Added OpenAI conversation compaction support
across core, framework, logging, and the API surface (#4053)
- **Multi-Customer & Org Hierarchy** — Logs and usage tracking now
support multiple customers, teams, and business units, including
business unit CRUD, team assignment, and governance endpoints in the
OpenAPI spec (#4066, #4041, #4082)
- **Provider-Level Governance** — Budgets & limits are now scope-aware
and can be applied at the virtual-key top level and per provider, wired
from the model configs table, with UI filters for scope and providers
(#3938, #3937, #3939, #3981, #3962)
- **Customer Budgets** — Customers support multiple budgets and
`calendar_aligned` budget windows (#3998, #3997)
- **Virtual Key Attribution & Controls** — Added a `created_by` user
attribution column and a `blacklisted_models` column for virtual key
provider configs (#3672, #3653)
- **Request Header Capture** — OTel and Maxim observability plugins
capture `request_headers` by pattern, with wildcard support (e.g.
`x-custom-*`); logging gained the same wildcard header capture (#4012,
#3958)
- **OTel Content Controls & Collectors** — New `disable_content_logging`
option drops message/tool content from exported spans, plus support for
multiple OTel collectors (#4064, #3894)
- **xAI x_search** — Added xAI `x_search` tool support (#3976)
- **URL Validation** — Added fetch URL validation with private-network
configuration and link-local blocking (#3947, #3991)
- **File Scheme Pricing URLs** — Pricing source URLs now accept the
`file://` scheme for air-gapped and self-hosted deployments (#4045)
- **Paginated Virtual Keys** — Virtual key fetching is paginated to
handle deployments with very large numbers of keys (#3957)
- **Client IP Resolution** — Resolve client IP from
`X-Forwarded-For`/`X-Real-IP` headers
- **SCIM Provisioning** — Added `attributeType`/`attributeValue` SCIM
provisioning fields
- **Helm/Config Schema** — Added `roles` RBAC governance config and
`per_user_oauth` MCP auth to the Helm chart and config schema (#4004,
#4009)
- **Log Navigation UI** — Added a "View logs" menu item to customer,
team, and virtual key tables, clickable links in log detail views, a
customer detail sheet, and a reusable `BudgetDisplay` component (#4073,
#4054, #4026, #4055)
- **Faster First Paint** — Added an inline loading shell to `#root`
before React mounts (#4063)
- **Materialized View Alias** — Added an `alias` column to the
materialized view with filter support (#4078)

## 🐞 Fixed

- **Fetch URL IP Checks** — Hardened fetch URL IP checks against SSRF
(#4092)
- **Mantle Model Matching** — Broadened Mantle model matching to all
`gpt` variants (#4091)
- **Empty Thinking Blocks** — Strip thinking blocks when the signature
is empty (#4079)
- **OpenAI Stream Usage** — Removed usage from the `responses.created`
event in the OpenAI stream (#4080)
- **Prompt Cache Key** — Set the prompt cache key from the Anthropic
integration (#4086)
- **Upstream Failure Status** — Map upstream connection failures to 502
instead of 400 (#3929) (thanks
[@chris-colinsky](https://github.com/chris-colinsky)!)
- **Gemini Schema Constraints** — Accept numeric schema integer
constraints for Gemini (#3994) (thanks
[@yanhao98](https://github.com/yanhao98)!)
- **Files Provider Param** — Accept the `?provider=` query param on `GET
/v1/files` (#3971) (thanks [@alexef](https://github.com/alexef)!)
- **Optional Batch Model** — Made the `model` field optional on `POST
/v1/batches` (#3973) (thanks [@alexef](https://github.com/alexef)!)
- **Helm Azure Config** — Added missing `azure_key_config` fields to the
Helm schema (#3996) (thanks
[@axelray-dev](https://github.com/axelray-dev)!)
- **Text Completion Chunk Model** — Added the missing `Model` field to
`TextCompletionChunkResponse` (#3970) (thanks
[@kuishou68](https://github.com/kuishou68)!)
- **MCP Inline stdio Env** — MCP stdio server configs accept inline
environment variable assignments (#3861) (thanks
[@Shushmitaaaa](https://github.com/Shushmitaaaa)!)
- **Orphaned Tool Results** — Orphaned tool results in the OpenAI to
Anthropic conversion flow are no longer rejected by the Anthropic API
(#3919)
- **Node Usage Reconciliation** — Added a monotonic `inc_number` log
cursor so node usage reconciliation does not skip late async log writes
(#3664)
- **Bedrock Output Assessments** — Corrected the type of
`outputAssessments` in Bedrock responses (#4028)
- **Model Pool Pricing Reloads** — Preserve non-pricing model pool
entries across pricing reloads (#3999)
- **Ghost Node Reconciliation** — Replicate the VK hierarchy flow for
ghost node reconciliation (#4088)
- **VK Double Usage Counting** — Fixed double usage counting when
creating a virtual key (#4070)
- **Model Config Lifecycle** — Cascade deletes for model configs and
removal of stale in-memory model configs (#4051, #4043)
- **FTS Index Cap** — Reduced the FTS index `left()` cap from 800k to
250k chars to stay within the tsvector limit (#4057)
- **Sync Worker Drift** — Reduced the sync worker ticker period to 5m to
prevent threshold drift (#4023)
- **Passthrough** — Fixed passthrough budgets, gated passthrough models
per VK, model extraction for Azure passthrough, and restricted
fallbacks/provider selection to the VK boundary (#3941, #3988, #3983,
#3924)
- **Provider Response Headers** — Strip provider response headers and
add a content-type filter (#3955, #4024)
- **Stream Handling** — Drain non-SSE stream readers and retry stale
connections (#3956, #3967)
- **Azure Claude** — Strip Azure diagnostic property for Claude models
(#3925)
- **Compat max_tokens** — Preserve chat `max_tokens` during param
filtering (#3992)
- **Raw Request Flag** — Removed the raw request flag from providers
that don't support it (#4058)
- **UI Fixes** — Standardized page container layout, virtual key model
configs UI, and dashboard chart tooltips (#4046, #4052, #4044)

## 🔧 Maintenance

- **Dependency Upgrades** — Bumped transitive `golang.org/x`
dependencies (crypto, net, sys, text) for Docker Scout CVE remediation
and `recharts` to 3.8.1; cascaded version bumps across all modules
(#3900, #4003)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants