Skip to main content
v1.5.12

Changelog

This release keeps the OSS transports base at v1.6.11 and moves the OSS core to v1.7.13. The enterprise work is a rework of identity provisioning: a new provider-wide “provisioning source” mode that makes SCIM the single owner of user identity, protection for SCIM-owned and admin-owned rows across OIDC login, token refresh, and periodic sync, and a fix for quota and virtual key APIs that were reading stale in-memory values instead of the config store.

✨ Features

  • Provisioning Source Mode (SCIM Only or SCIM Plus Login Claims) - A new provider-wide claims_sync_mode setting replaces the old per-user provenance checks. In “SCIM only” mode, IdP login claims never create or modify a user: an unknown user hitting the OIDC callback or a token refresh gets a 403 with a “provision via SCIM” message, and role, team, and business unit sync is skipped entirely. The wizard’s attribute-mapping step exposes this as a “Provisioning source” dropdown. Docs
  • Single Claims Sync Funnel - Role, team, and business unit sync from IdP claims now runs through one syncUserInfoFromClaims entry point used by login, dashboard token refresh, and the periodic sweep, instead of three near-identical code paths. Memberships are applied before the role so a role-driven auto-assignment sees the final team set, and the periodic sweep refreshes each session once instead of twice. Docs
  • Manual Provenance for Team to Business Unit Edges - A team to business unit edge assigned through the business unit API or a config reconcile is now stamped source=manual. The OIDC claims sync can no longer clear or overwrite it on a truncated groups claim, while an authoritative SCIM push may still adopt it and take over its lifecycle. Docs

🌎 Open Source Features

  • Gemini Server-Side Tool Calls with Thought Signature Fidelity - When includeServerSideToolInvocations is on, the toolCall and toolResponse parts Gemini returns for built-in tools such as Google Search are now mapped to web_search_call items with Gemini’s own call IDs and queries, instead of being dropped silently. Unmapped built-in tool types survive the native round trip, and each thoughtSignature appears exactly once across the reconstructed parts so a replayed turn is accepted. Docs
  • Per Model Family URL Resolution on Vertex - A gs:// URI is now forwarded to Gemini and Gemma as fileData.fileUri, which Vertex resolves under the caller’s own project IAM and which is the only form that keeps multi-hundred-megabyte video inputs viable. For Claude on Vertex, which accepts base64 sources only, the object is read from Cloud Storage using the request key’s own Google credentials. http(s) sources are still always fetched. Docs

🐞 Fixed

  • Quota and Virtual Key APIs Returned Stale Values - QuotaGovernanceForVK read budgets and rate limits from the in-memory local store instead of the config store, so the quota API and the get-virtual-keys API could report stale numbers. Lookups now go to the config store and errors are propagated instead of being swallowed.
  • SCIM Group List Included Non-SCIM Memberships - The SCIM list-group response no longer includes manually assigned or OIDC-owned memberships. Some IdPs treat a “Push now” on an existing client as a reconcile and send a patch based on what the list returned, which silently adopted those memberships as SCIM-owned.
  • SCIM Profile Wiped by an OIDC Login - CreateOrUpdateUserFromOAuth overwrote ParsedProfile wholesale, so a thin login ID token erased SCIM-imported attributes. The upsert now takes a PreserveProfile option decided by the existing user’s provenance, and the provenance lookup also covers soft-deleted rows so reactivating a SCIM user keeps their profile. A provenance lookup error fails closed.
  • SCIM-Owned Rows Not Reclaimed in Both Mode - In “both” mode with SCIM disabled, claims are authoritative again: a SCIM-owned team membership not matched by the current claims is reclaimed rather than spared forever. In SCIM-only mode those rows stay foreign-preserved and an OIDC login is a no-op for memberships.
  • OIDC Login Mutated a SCIM-Synced Identity - Login gated only the role on SCIM ownership while team and business unit sync ran unconditionally. The whole identity is now gated, on login, on refresh, and on the periodic sweep.
  • Identity Not Re-Synced on Dashboard Token Refresh - oauthRefresh rotated the token and cookie but never re-synced role, team, or business unit, so an IdP change only took effect after a full re-login.
  • Keycloak Auth Cookie Used the Wrong Token - The Keycloak auth cookie stores the access token again, not the ID token. Keycloak’s native role claims (realm_access.roles and resource_access.{client}.roles) live in the access token, so the earlier switch dropped them. This reverses the ID-token change shipped in v1.5.11.
  • Virtual Keys Dropped When a Role Profile Was Applied - applyRoleProfile now passes preserveVirtualKeys=true for existing users on every entry point (SCIM, OIDC, refresh, sweep, and import), which was previously inconsistent per path.
  • Manual Memberships Not Adopted by SCIM Pushes - A manually assigned team membership is now adopted and re-stamped as SCIM-owned by SyncUserTeamMemberships and RelabelTeamMembersAsScimGroup, not only by the live SCIM user PUT, so the bulk sync and group-push paths behave the same way.
  • Racing Role Sync on Token Refresh - RefreshToken no longer spawns its own role-sync goroutine. All three callers already sync after it, so the extra goroutine only raced them.
  • OIDC Session Floor Not Applied on Refresh - OIDCSessionMinTTL moved to core/scim and is now applied in TokenRefresher.RefreshToken as well, matching the dashboard refresh and session-create paths.
  • Bedrock Rejected Non-PDF Documents - Document format was resolved only from the Bifrost-specific file_type field, which OpenAI-compatible clients never send, so XLSX, DOCX, CSV, and Markdown all fell through to a hardcoded pdf default and were rejected. Format now resolves in priority order: file_type, data URL media type, filename extension, then pdf. Data URL parsing also handles media type parameters and uppercase headers, and the Responses path fetches file_url instead of emitting an empty document block. Docs
  • Bedrock Rerank Rejected Bare Model IDs - Rerank is the one Bedrock surface that names its model by ARN, so a bare ID such as amazon.rerank-v1:0 returned a 400. The foundation-model ARN is now synthesized from the resolved region, with the partition derived from it (aws, aws-cn, aws-us-gov), and an explicit ARN still passes through untouched. Docs
  • Gemini 3 Tools Advertised but Never Invoked - Reasoning depth on Gemini 3 is selected per model, not per family. Bifrost branched only on whether the name contained “pro”, so an unsupported thinkingLevel was rejected and thinkingBudget: 0 suppressed the internal reasoning Gemini 3 needs for function selection. A per-model support table now clamps a requested level to the nearest supported rung, and “none” effort floors at the model’s lowest level instead of zeroing the budget. Docs
  • Gemini Response With No Candidates - A thinking model that spends its whole output budget before emitting a token is a successful 200 with an empty answer, but the candidate was dropped, leaving a body with no candidates key that Gemini-shaped clients dereference blind. A candidate carrying its finish reason is now always emitted, and payload-free parts that marshal to {} are dropped when assembling it.
  • Reasoning Blocks Opened as Redacted - A reasoning stream item carrying both an encrypted payload and a visible summary was opened as a redacted_thinking block, so Anthropic SDK clients threw “Content block is not a thinking block” on the signature delta or silently dropped the summary text. redacted_thinking is now used only when the encrypted payload is the sole content.
  • JSON Schema Key Order Re-Sorted - Structured Outputs generates fields in the order the schema declares them, so re-sorting a caller’s schema changes model behavior. Declared key order is now preserved through response_format handling across the shared schema layer and the provider paths that rewrite it.
  • URL Sources Failed Instead of Passing Through - A gs://, s3://, or scheme-less source that Bifrost cannot download is no longer treated as a failure on the OpenAI and native Anthropic paths. Only http(s) is fetched; other schemes travel as {"type":"url"} so the provider decides whether it can resolve them. file_url is also no longer stripped from OpenAI-shaped chat file blocks on marshal, which used to produce {"type":"file","file":{}} and an unrelated “missing file_id” complaint.

🗄️ Database Migrations

No new database migrations in this release, in either the OSS layer or the enterprise repo.

📀 Base OSS version

transports/v1.6.11

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