diff --git a/docs/changelogs/helm-v2.1.28.mdx b/docs/changelogs/helm-v2.1.28.mdx new file mode 100644 index 00000000000..1a129e10b23 --- /dev/null +++ b/docs/changelogs/helm-v2.1.28.mdx @@ -0,0 +1,17 @@ +--- +title: "v2.1.28" +description: "Helm v2.1.28 changelog - 2026-07-10" +--- + + + +## Changelog + +- `bifrost.auditLogs.objectStorage` for archiving audit events to S3/GCS. Each flushed batch is written as a JSONL object (`{prefix}/audit-logs/YYYY/MM/DD/HH/{id}.jsonl`); set `compress: true` to gzip the output (`.jsonl.gz`). Supports `type` (s3/gcs), `bucket`, `prefix`, `compress`, and full S3 credential fields (`region`, `endpoint`, `accessKeyId`, `secretAccessKey`, `sessionToken`, `roleArn`, `forcePathStyle`) and GCS fields (`projectId`, `credentialsJson`). Renders into `audit_logs.object_storage`. +- `force_single_region` on `bifrost.providers.vertex.keys[*].vertex_key_config` — when `true`, skips automatic promotion of multi-region-only models to a multi-region endpoint. Enable for provisioned throughput. Renders into `vertex_key_config.force_single_region`. +- `calendar_aligned` on `bifrost.accessProfiles[*]` (top-level) — snaps all budget and rate-limit reset windows to calendar boundaries for the profile. Passes through into `access_profiles[*].calendar_aligned`. +- `calendar_aligned` on `bifrost.accessProfiles[*].budgets[*]` and `bifrost.accessProfiles[*].provider_configs[*].budgets[*]` — schema previously blocked this field; now matches parity with `governance.budgets[*].calendar_aligned`. +- `calendar_aligned` on `bifrost.governance.virtualKeys[*]` — was accepted by schema but not rendered into config. Now correctly emits `virtual_keys[*].calendar_aligned` in the generated config. +- `allowOnAllVirtualKeys` on `bifrost.mcp.clientConfigs[*]` — grants this MCP server access to all virtual keys without per-key assignment. Already wired in the template; now documented in `values.yaml`. Renders into `mcp.client_configs[*].allow_on_all_virtual_keys`. + + diff --git a/docs/docs.json b/docs/docs.json index 002e321a501..0dfd2de56f8 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -948,6 +948,7 @@ "item": "Helm", "icon": "box", "pages": [ + "changelogs/helm-v2.1.28", "changelogs/helm-v2.1.26", "changelogs/helm-v2.1.25", "changelogs/helm-v2.1.24", diff --git a/helm-charts/bifrost/README.md b/helm-charts/bifrost/README.md index e87ac861f9e..27576d8b1e9 100644 --- a/helm-charts/bifrost/README.md +++ b/helm-charts/bifrost/README.md @@ -8,6 +8,16 @@ Official Helm charts for deploying [Bifrost](https://github.com/maximhq/bifrost) ## Changelog +### Upcoming + +- Added `bifrost.auditLogs.objectStorage` for archiving audit events to S3/GCS. Supports `type` (s3/gcs), `bucket`, `prefix`, `compress`, and full S3 credential fields (`region`, `endpoint`, `accessKeyId`, `secretAccessKey`, `sessionToken`, `roleArn`, `forcePathStyle`) and GCS fields (`projectId`, `credentialsJson`). Renders into `audit_logs.object_storage`. +- Added `force_single_region` to `bifrost.providers.vertex.keys[*].vertex_key_config`. When `true`, skips automatic promotion of multi-region-only models to a multi-region endpoint. Enable for provisioned throughput. Renders into `vertex_key_config.force_single_region`. +- Added `calendar_aligned` to `bifrost.accessProfiles[*]` (top-level on each profile). Snaps all budget and rate-limit reset windows to calendar boundaries for the profile. Passes through directly into `access_profiles[*].calendar_aligned`. +- Added `calendar_aligned` to `bifrost.accessProfiles[*].budgets[*]` and `bifrost.accessProfiles[*].provider_configs[*].budgets[*]`. Schema previously blocked this field via `additionalProperties: false`; now parity with `governance.budgets[*].calendar_aligned`. +- Added `calendar_aligned` rendering for `bifrost.governance.virtualKeys[*].calendar_aligned`. Was in schema but not rendered into config. Now emits `virtual_keys[*].calendar_aligned` in the generated config. +- Added `allowOnAllVirtualKeys` to the `bifrost.mcp.clientConfigs[*]` example documentation. Field was already wired in the template; now visible in `values.yaml`. Renders into `mcp.client_configs[*].allow_on_all_virtual_keys`. +- Documented `calendar_aligned` in the `bifrost.governance.budgets[*]` example. Was already schema-supported; now shown in the `values.yaml` commented example. + ### 2.1.27 - Added `bifrost.schemaUrl` to override the generated `config.json` `$schema` location for isolated deployments. It accepts HTTP(S), `file://`, or filesystem paths. When set, it is also exported as `BIFROST_SCHEMA_URL` in the pod; when empty (default), the env var is not injected and the public schema URL is used. diff --git a/helm-charts/bifrost/templates/_helpers.tpl b/helm-charts/bifrost/templates/_helpers.tpl index cb933985ac9..f5ad499eddc 100644 --- a/helm-charts/bifrost/templates/_helpers.tpl +++ b/helm-charts/bifrost/templates/_helpers.tpl @@ -512,6 +512,7 @@ false {{- if .customer_id }}{{- $_ := set $vk "customer_id" .customer_id }}{{- end }} {{- if hasKey . "access_profile_id" }}{{- $_ := set $vk "access_profile_id" .access_profile_id }}{{- end }} {{- if .rate_limit_id }}{{- $_ := set $vk "rate_limit_id" .rate_limit_id }}{{- end }} +{{- if hasKey . "calendar_aligned" }}{{- $_ := set $vk "calendar_aligned" .calendar_aligned }}{{- end }} {{- if .provider_configs }}{{- $_ := set $vk "provider_configs" .provider_configs }}{{- end }} {{- if .mcp_configs }}{{- $_ := set $vk "mcp_configs" .mcp_configs }}{{- end }} {{- $vks = append $vks $vk }} @@ -1554,7 +1555,49 @@ false {{- if .Values.bifrost.auditLogs.hmacKey }} {{- $_ := set $auditLogs "hmac_key" .Values.bifrost.auditLogs.hmacKey }} {{- end }} -{{- if or (hasKey $auditLogs "disabled") $auditLogs.hmac_key }} +{{- if .Values.bifrost.auditLogs.objectStorage }} +{{- $aos := .Values.bifrost.auditLogs.objectStorage }} +{{- $aosConfig := dict "type" $aos.type "bucket" $aos.bucket }} +{{- if $aos.prefix }} +{{- $_ := set $aosConfig "prefix" $aos.prefix }} +{{- end }} +{{- if $aos.compress }} +{{- $_ := set $aosConfig "compress" true }} +{{- end }} +{{- if eq $aos.type "s3" }} +{{- if $aos.region }} +{{- $_ := set $aosConfig "region" $aos.region }} +{{- end }} +{{- if $aos.endpoint }} +{{- $_ := set $aosConfig "endpoint" $aos.endpoint }} +{{- end }} +{{- if $aos.accessKeyId }} +{{- $_ := set $aosConfig "access_key_id" $aos.accessKeyId }} +{{- end }} +{{- if $aos.secretAccessKey }} +{{- $_ := set $aosConfig "secret_access_key" $aos.secretAccessKey }} +{{- end }} +{{- if $aos.sessionToken }} +{{- $_ := set $aosConfig "session_token" $aos.sessionToken }} +{{- end }} +{{- if $aos.roleArn }} +{{- $_ := set $aosConfig "role_arn" $aos.roleArn }} +{{- end }} +{{- if $aos.forcePathStyle }} +{{- $_ := set $aosConfig "force_path_style" true }} +{{- end }} +{{- end }} +{{- if eq $aos.type "gcs" }} +{{- if $aos.projectId }} +{{- $_ := set $aosConfig "project_id" $aos.projectId }} +{{- end }} +{{- if $aos.credentialsJson }} +{{- $_ := set $aosConfig "credentials_json" $aos.credentialsJson }} +{{- end }} +{{- end }} +{{- $_ := set $auditLogs "object_storage" $aosConfig }} +{{- end }} +{{- if or (hasKey $auditLogs "disabled") $auditLogs.hmac_key $auditLogs.object_storage }} {{- $_ := set $config "audit_logs" $auditLogs }} {{- end }} {{- end }} diff --git a/helm-charts/bifrost/values.schema.json b/helm-charts/bifrost/values.schema.json index 6a56961f5d8..a76dcd21c83 100644 --- a/helm-charts/bifrost/values.schema.json +++ b/helm-charts/bifrost/values.schema.json @@ -3088,6 +3088,11 @@ }, "reset_duration": { "type": "string" + }, + "calendar_aligned": { + "type": "boolean", + "description": "Deprecated: set calendar_aligned on the parent access profile instead. The reconciler promotes any true value here to the profile's top-level calendar_aligned at load time.", + "default": false } }, "required": ["id", "max_limit", "reset_duration"], @@ -3146,6 +3151,11 @@ }, "reset_duration": { "type": "string" + }, + "calendar_aligned": { + "type": "boolean", + "description": "Deprecated: set calendar_aligned on the parent access profile instead. The reconciler promotes any true value here to the profile's top-level calendar_aligned at load time.", + "default": false } }, "required": ["id", "max_limit", "reset_duration"], @@ -3230,6 +3240,11 @@ "required": ["mcp_client_id", "tool_name", "action"], "additionalProperties": false } + }, + "calendar_aligned": { + "type": "boolean", + "description": "Snap budget and rate-limit reset windows to clean calendar boundaries (day, week, month, year) for this profile", + "default": false } }, "required": ["name"], @@ -3244,6 +3259,69 @@ }, "hmacKey": { "type": "string" + }, + "objectStorage": { + "type": "object", + "description": "Optional object storage for archiving audit events to S3/GCS. Each flushed batch is written as a gzipped JSONL object at {prefix}/audit-logs/YYYY/MM/DD/HH/{id}.jsonl[.gz], in addition to the database.", + "properties": { + "type": { + "type": "string", + "enum": ["s3", "gcs"], + "description": "Object storage backend type" + }, + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "prefix": { + "type": "string", + "description": "Base key path for stored audit objects; audit-logs/ is appended under it", + "default": "bifrost" + }, + "compress": { + "type": "boolean", + "description": "Enable gzip compression for stored objects", + "default": false + }, + "region": { + "type": "string", + "description": "AWS region (S3 only)" + }, + "endpoint": { + "type": "string", + "description": "Custom S3-compatible endpoint for MinIO/R2 (S3 only)" + }, + "accessKeyId": { + "type": "string", + "description": "AWS access key ID; omit to use default credential chain (S3 only)" + }, + "secretAccessKey": { + "type": "string", + "description": "AWS secret access key (S3 only)" + }, + "sessionToken": { + "type": "string", + "description": "AWS STS session token (S3 only, optional)" + }, + "roleArn": { + "type": "string", + "description": "AWS IAM role ARN to assume via STS (S3 only)" + }, + "forcePathStyle": { + "type": "boolean", + "description": "Use path-style S3 URLs; required for MinIO (S3 only)", + "default": false + }, + "projectId": { + "type": "string", + "description": "GCP project ID override (GCS only)" + }, + "credentialsJson": { + "type": "string", + "description": "GCP service account credentials JSON or file path; omit for Application Default Credentials (GCS only)" + } + }, + "required": ["type", "bucket"] } } }, @@ -4661,6 +4739,11 @@ "type": "string", "description": "Authentication credentials (can use env. prefix)" }, + "force_single_region": { + "type": "boolean", + "description": "When true, always call the configured region and skip automatic promotion of multi-region-only models to a multi-region endpoint. Enable for provisioned throughput.", + "default": false + }, "deployments": { "type": "object", "additionalProperties": { @@ -5568,6 +5651,11 @@ "type": "string", "description": "Authentication credentials (can use env. prefix)" }, + "force_single_region": { + "type": "boolean", + "description": "When true, always call the configured region and skip automatic promotion of multi-region-only models to a multi-region endpoint. Enable for provisioned throughput.", + "default": false + }, "deployments": { "type": "object", "additionalProperties": { diff --git a/helm-charts/bifrost/values.yaml b/helm-charts/bifrost/values.yaml index 53b53f834a4..6c0a72a14f2 100644 --- a/helm-charts/bifrost/values.yaml +++ b/helm-charts/bifrost/values.yaml @@ -406,6 +406,7 @@ bifrost: # project_id: "my-gcp-project" # region: "us-central1" # auth_credentials: "env.GOOGLE_CREDENTIALS" + # force_single_region: false # Set true for provisioned throughput to skip multi-region promotion # # # AWS Bedrock example (requires bedrock_key_config) # bedrock: @@ -484,6 +485,9 @@ bifrost: # # oauth_config is registered via the API (POST /api/mcp/clients), not configured here. # authType: "oauth" # oauthConfigId: "my-oauth-config-id" # ID of the OAuth config created in Bifrost + # # When true, this MCP server is accessible to all virtual keys without explicit per-key assignment. + # # If a virtual key has an explicit MCP config for this server, that config takes precedence. + # allowOnAllVirtualKeys: false # toolSyncInterval: "10m" # Global tool sync interval (Go duration string, e.g. "10m", "1h", "0s") # Tool manager configuration toolManagerConfig: @@ -705,6 +709,7 @@ bifrost: # - id: "budget-1" # max_limit: 100 # reset_duration: "1M" # Supports: 30s, 5m, 1h, 1d, 1w, 1M, 1Y + # calendar_aligned: false # Snap reset to calendar boundaries (e.g. 1M resets on the 1st) rateLimits: [] # - id: "rate-limit-1" # token_max_limit: 100000 @@ -753,6 +758,7 @@ bifrost: # value: "sk-bf-..." # Optional - auto-generated if omitted # is_active: true # expires_at: "2026-12-31T23:59:59Z" # Optional RFC3339 expiry; requests rejected once passed. Omit for no expiry + # calendar_aligned: false # Snap all budget resets for this key to calendar boundaries # team_id: "team-1" # Mutually exclusive with customer_id # customer_id: "" # Mutually exclusive with team_id # rate_limit_id: "rate-limit-1" @@ -1033,11 +1039,13 @@ bifrost: # - name: "platform-default" # description: "Default platform profile" # is_active: true + # calendar_aligned: false # Snap all budget/rate-limit resets to calendar boundaries # tags: ["platform", "default"] # budgets: # - id: "ap-budget-1" # max_limit: 100 # reset_duration: "1M" + # calendar_aligned: false # Snap this budget's reset to calendar boundaries # rate_limit: # id: "ap-rate-limit-1" # token_max_limit: 200000 @@ -1059,6 +1067,28 @@ bifrost: auditLogs: disabled: false hmacKey: "" + # Object storage archival for audit events (optional) + # When configured, each flushed batch of audit events is written as a JSONL object at + # {prefix}/audit-logs/YYYY/MM/DD/HH/{id}.jsonl in addition to the DB. + # Set compress: true to gzip the objects ({id}.jsonl.gz). + # objectStorage: + # type: s3 # Options: s3, gcs + # bucket: "" # Bucket name + # prefix: bifrost # Base key path (audit-logs/ is appended under it) + # compress: false # Enable gzip compression for stored objects + # + # # S3 configuration (when type is s3) + # region: us-east-1 + # endpoint: "" # Custom endpoint for MinIO/R2 + # accessKeyId: "" # Leave empty to use default AWS credential chain + # secretAccessKey: "" + # sessionToken: "" # AWS STS session token (optional) + # roleArn: "" # AWS IAM role ARN to assume via STS + # forcePathStyle: false # Set true for MinIO + # + # # GCS configuration (when type is gcs) + # projectId: "" + # credentialsJson: "" # Service account JSON, omit for default credentials # Large payload optimization - streams large payloads without full materialization # largePayloadOptimization: diff --git a/transports/config.schema.json b/transports/config.schema.json index 458d5319c78..221eb179c3f 100644 --- a/transports/config.schema.json +++ b/transports/config.schema.json @@ -947,6 +947,290 @@ }, "additionalProperties": false }, + "alerting": { + "type": "object", + "description": "Enterprise alerting configuration for governance-backed alert channels, rules, history retention, and outbound webhook URL validation.", + "properties": { + "history_retention_days": { + "type": "integer", + "minimum": 0, + "default": 365, + "description": "Number of days to retain alert history. Omit to use the default. Set to 0 to disable retention pruning." + }, + "webhook_network": { + "type": "object", + "description": "Outbound URL validation controls for Slack, Microsoft Teams, and generic webhook alert channels.", + "properties": { + "allow_http": { + "type": "boolean", + "default": false, + "description": "Allow alert webhooks to use http URLs. By default only https URLs are accepted." + }, + "allow_private_network": { + "type": "boolean", + "default": false, + "description": "Allow alert webhooks to target private or loopback network addresses. Useful for local testing." + } + }, + "additionalProperties": false + }, + "channels": { + "type": "array", + "description": "Declarative alert channels synced into the enterprise config store at startup.", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Stable alert channel ID." + }, + "name": { + "type": "string", + "description": "Operator-facing alert channel name." + }, + "description": { + "type": "string", + "description": "Optional alert channel description." + }, + "type": { + "type": "string", + "enum": ["slack", "microsoft_teams", "pagerduty", "webhook"], + "description": "Alert channel type." + }, + "enabled": { + "type": "boolean", + "description": "Whether this alert channel can receive notifications." + }, + "cooldown_seconds": { + "type": "integer", + "minimum": 0, + "description": "Minimum seconds between sends for this channel. Set to 0 for no channel-level cooldown." + }, + "config": { + "type": "object", + "description": "Channel-specific delivery configuration. Slack and Microsoft Teams use webhook_url or url. PagerDuty uses routing_key or integration_key. Generic webhook uses url or webhook_url, plus optional string headers.", + "additionalProperties": true + } + }, + "required": ["id", "name", "type", "enabled"], + "allOf": [ + { + "if": { + "properties": { "type": { "const": "slack" } }, + "required": ["type"] + }, + "then": { + "properties": { + "config": { + "type": "object", + "properties": { + "webhook_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "Slack incoming webhook URL." + }, + "url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "Alias for webhook_url." + } + }, + "anyOf": [{ "required": ["webhook_url"] }, { "required": ["url"] }], + "additionalProperties": true + } + }, + "required": ["config"] + } + }, + { + "if": { + "properties": { "type": { "const": "microsoft_teams" } }, + "required": ["type"] + }, + "then": { + "properties": { + "config": { + "type": "object", + "properties": { + "webhook_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "Microsoft Teams incoming webhook or workflow URL." + }, + "url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "Alias for webhook_url." + } + }, + "anyOf": [{ "required": ["webhook_url"] }, { "required": ["url"] }], + "additionalProperties": true + } + }, + "required": ["config"] + } + }, + { + "if": { + "properties": { "type": { "const": "pagerduty" } }, + "required": ["type"] + }, + "then": { + "properties": { + "config": { + "type": "object", + "properties": { + "routing_key": { + "type": "string", + "description": "PagerDuty Events API v2 integration key." + }, + "integration_key": { + "type": "string", + "description": "Alias for routing_key." + } + }, + "anyOf": [{ "required": ["routing_key"] }, { "required": ["integration_key"] }], + "additionalProperties": true + } + }, + "required": ["config"] + } + }, + { + "if": { + "properties": { "type": { "const": "webhook" } }, + "required": ["type"] + }, + "then": { + "properties": { + "config": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "Generic webhook URL." + }, + "webhook_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "Alias for url." + }, + "headers": { + "type": "object", + "description": "Optional headers to send with the webhook request. Sensitive hop-by-hop headers are ignored at send time.", + "additionalProperties": { "type": "string" } + } + }, + "anyOf": [{ "required": ["url"] }, { "required": ["webhook_url"] }], + "additionalProperties": true + } + }, + "required": ["config"] + } + } + ], + "additionalProperties": false + } + }, + "rules": { + "type": "array", + "description": "Declarative alert rules synced into the enterprise config store at startup.", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Stable alert rule ID." + }, + "name": { + "type": "string", + "description": "Operator-facing alert rule name." + }, + "description": { + "type": "string", + "description": "Optional alert rule description." + }, + "enabled": { + "type": "boolean", + "description": "Whether this alert rule is evaluated." + }, + "scope_type": { + "type": "string", + "enum": ["virtual_key", "team", "customer"], + "description": "Governance owner scope to evaluate." + }, + "scope_id": { + "type": "string", + "description": "ID of the virtual key, team, or customer referenced by scope_type." + }, + "cel_expression": { + "type": "string", + "description": "CEL expression evaluated against alerting usage variables such as budget_usage_percent, budget_spent, request_usage, token_usage, and rate-limit percentages." + }, + "query": { + "type": "object", + "description": "Optional UI query-builder representation of cel_expression.", + "additionalProperties": true + }, + "cooldown_seconds": { + "type": "integer", + "minimum": 0, + "default": 60, + "description": "Minimum seconds between notifications for this rule. Omit to use the default. Set to 0 for no rule cooldown." + }, + "notify_once_per_reset_cycle": { + "type": "boolean", + "default": false, + "description": "When true, notify at most once per matched budget or rate-limit reset cycle. This overrides rule cooldown to 0." + }, + "channel_ids": { + "type": "array", + "description": "IDs of alert channels to notify when this rule matches.", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1 + }, + "target_type": { + "type": "string", + "enum": ["budget"], + "description": "Optional explicit target type. Use budget with target_id to evaluate one specific budget; omit to evaluate all budgets for the scope." + }, + "target_id": { + "type": "string", + "description": "ID of the explicit target referenced by target_type." + } + }, + "required": ["id", "name", "enabled", "scope_type", "scope_id", "cel_expression", "channel_ids"], + "allOf": [ + { + "if": { + "required": ["target_type"] + }, + "then": { + "required": ["target_id"] + } + }, + { + "if": { + "required": ["target_id"] + }, + "then": { + "required": ["target_type"] + } + } + ], + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, "mcp": { "type": "object", "description": "Model Context Protocol configuration",