Plugins are configured under bifrost.plugins. Each plugin is independently enabled/disabled. Pre-hooks run in registration order; post-hooks run in reverse order.
Telemetry, logging, and governance are auto-loaded built-ins - they are always active and do not need to be explicitly enabled. Their configuration lives in bifrost.client.* and bifrost.governance.*, not in the plugins block.The plugins block controls the opt-in plugins: semanticCache, otel, datadog, maxim, and custom plugins.
bifrost:
plugins:
semanticCache:
enabled: false
otel:
enabled: false
datadog:
enabled: false
# Enable an opt-in plugin at install time
helm install bifrost bifrost/bifrost \
--set image.tag=v1.4.11 \
--set bifrost.plugins.otel.enabled=true
# Or upgrade to enable a plugin without touching other values
helm upgrade bifrost bifrost/bifrost \
--reuse-values \
--set bifrost.plugins.semanticCache.enabled=true
Telemetry
Logging
Governance
Semantic Cache
OpenTelemetry
Datadog
Maxim
Custom Plugin
Telemetry (Prometheus)
Telemetry is always active - it cannot be disabled. You do not need to set bifrost.plugins.telemetry.enabled.
Exposes Prometheus metrics at GET /metrics. Custom labels are set via bifrost.client.prometheusLabels:bifrost:
client:
prometheusLabels:
- "environment=production"
- "region=us-east-1"
# Verify metrics are exposed
kubectl port-forward svc/bifrost 8080:8080 &
curl http://localhost:8080/metrics | head -30
With Prometheus Push Gateway (recommended for multi-replica / HA setups where pull-based scraping can miss pods):bifrost:
plugins:
telemetry:
enabled: true
config:
push_gateway:
enabled: true
push_gateway_url: "http://prometheus-pushgateway.monitoring.svc.cluster.local:9091"
job_name: "bifrost"
instance_id: "" # auto-derived from pod name if empty
push_interval: 15
basic_auth:
username: ""
password: ""
ServiceMonitor for Prometheus Operator:serviceMonitor:
enabled: true
interval: 30s
scrapeTimeout: 10s
namespace: monitoring # namespace where Prometheus is deployed
Request/Response Logging
Logging is auto-loaded when bifrost.client.enableLogging: true and a log store is configured. You do not need to set bifrost.plugins.logging.enabled.
Configure logging via the client block:| Parameter | Description | Default |
|---|
bifrost.client.enableLogging | Enable request/response logging | true |
bifrost.client.disableContentLogging | Strip message body from logs (HIPAA/PCI) | false |
bifrost.client.loggingHeaders | HTTP headers to capture in log metadata | [] |
bifrost:
client:
enableLogging: true
disableContentLogging: false # set true for HIPAA/compliance
loggingHeaders:
- "x-request-id"
- "x-user-id"
- "x-team-id"
# Verify logs are being written
kubectl port-forward svc/bifrost 8080:8080 &
curl -s "http://localhost:8080/api/logs?limit=5" | jq .
See Client Configuration for the full reference.Governance
Governance is always active for OSS deployments. You do not need to set bifrost.plugins.governance.enabled.
Virtual key enforcement is controlled by the client block:| Parameter | Description | Default |
|---|
bifrost.client.enforceAuthOnInference | Require a virtual key (x-bf-vk) on every inference request | false |
bifrost:
client:
enforceAuthOnInference: true # require virtual key on all inference requests
Define virtual keys, budgets, rate limits, and routing rules in bifrost.governance.*. See the Governance page.Semantic Cache
Caches LLM responses using vector similarity so semantically equivalent prompts return cached answers.Two modes:
- Semantic mode (
dimension > 1): uses an embedding model + vector store for similarity search
- Direct / hash mode (
dimension: 1): exact-match hash-based caching, no embedding model needed
| Parameter | Description | Default |
|---|
bifrost.plugins.semanticCache.enabled | Enable semantic caching | false |
bifrost.plugins.semanticCache.version | Plugin config version for DB-backed update tracking (1 to 32767) | 1 |
bifrost.plugins.semanticCache.config.provider | Embedding provider | "openai" |
bifrost.plugins.semanticCache.config.embedding_model | Embedding model name | "text-embedding-3-small" |
bifrost.plugins.semanticCache.config.dimension | Embedding dimension (1 = direct/hash mode) | 1536 |
bifrost.plugins.semanticCache.config.threshold | Cosine similarity threshold (0–1) | 0.8 |
bifrost.plugins.semanticCache.config.ttl | Cache entry TTL (Go duration) | "5m" |
bifrost.plugins.semanticCache.config.conversation_history_threshold | Number of past messages to include in cache key | 3 |
bifrost.plugins.semanticCache.config.cache_by_model | Include model name in cache key | true |
bifrost.plugins.semanticCache.config.cache_by_provider | Include provider name in cache key | true |
bifrost.plugins.semanticCache.config.exclude_system_prompt | Exclude system prompt from cache key | false |
Semantic mode (with OpenAI embeddings + Weaviate):kubectl create secret generic semantic-cache-secret \
--from-literal=openai-key='sk-your-openai-embedding-key'
# semantic-cache-values.yaml
image:
tag: "v1.4.11"
vectorStore:
enabled: true
type: weaviate
weaviate:
enabled: true
persistence:
size: 20Gi
bifrost:
plugins:
semanticCache:
enabled: true
config:
provider: "openai"
keys:
- value: "env.SEMANTIC_CACHE_OPENAI_KEY"
weight: 1
embedding_model: "text-embedding-3-small"
dimension: 1536
threshold: 0.85
ttl: "1h"
conversation_history_threshold: 5
cache_by_model: true
cache_by_provider: true
providerSecrets:
semantic-cache-key:
existingSecret: "semantic-cache-secret"
key: "openai-key"
envVar: "SEMANTIC_CACHE_OPENAI_KEY"
helm install bifrost bifrost/bifrost -f semantic-cache-values.yaml
Direct / hash mode (no embedding provider needed):bifrost:
plugins:
semanticCache:
enabled: true
config:
dimension: 1 # triggers hash-based exact matching
ttl: "30m"
cache_by_model: true
cache_by_provider: true
The vector store (vectorStore.*) must be configured and enabled for semantic mode. Direct/hash mode works without a vector store but still requires a storage backend.
OpenTelemetry (OTel)
Sends distributed traces and push-based metrics to any OTLP-compatible collector (Jaeger, Tempo, Honeycomb, etc.).| Parameter | Description | Default |
|---|
bifrost.plugins.otel.enabled | Enable OTel tracing | false |
bifrost.plugins.otel.version | Plugin config version for DB-backed update tracking (1 to 32767) | 1 |
bifrost.plugins.otel.config.service_name | Service name in traces | "bifrost" |
bifrost.plugins.otel.config.collector_url | OTLP collector endpoint | "" |
bifrost.plugins.otel.config.trace_type | Trace type (genai_extension, vercel, or open_inference) | "genai_extension" |
bifrost.plugins.otel.config.protocol | Transport protocol (grpc or http) | "grpc" |
bifrost.plugins.otel.config.metrics_enabled | Enable OTLP push-based metrics | false |
bifrost.plugins.otel.config.metrics_endpoint | OTLP metrics endpoint | "" |
bifrost.plugins.otel.config.metrics_push_interval | Push interval in seconds | 15 |
bifrost.plugins.otel.config.headers | Custom headers for the collector | {} |
bifrost.plugins.otel.config.insecure | Skip TLS verification | false |
bifrost.plugins.otel.config.tls_ca_cert | Path to CA cert for TLS | "" |
# otel-values.yaml
image:
tag: "v1.4.11"
bifrost:
plugins:
otel:
enabled: true
config:
service_name: "bifrost-production"
collector_url: "otel-collector.observability.svc.cluster.local:4317"
trace_type: "genai_extension"
protocol: "grpc"
insecure: true # set false in production with a proper cert
metrics_enabled: true
metrics_endpoint: "otel-collector.observability.svc.cluster.local:4317"
metrics_push_interval: 15
headers:
x-honeycomb-team: "env.HONEYCOMB_API_KEY"
helm upgrade bifrost bifrost/bifrost --reuse-values -f otel-values.yaml
With authentication headers from a Kubernetes Secret:kubectl create secret generic otel-credentials \
--from-literal=api-key='your-honeycomb-or-grafana-key'
bifrost:
plugins:
otel:
enabled: true
config:
collector_url: "api.honeycomb.io:443"
protocol: "grpc"
headers:
x-honeycomb-team: "env.OTEL_API_KEY"
providerSecrets:
otel-key:
existingSecret: "otel-credentials"
key: "api-key"
envVar: "OTEL_API_KEY"
Datadog APM
Sends traces to a Datadog Agent running in the cluster.| Parameter | Description | Default |
|---|
bifrost.plugins.datadog.enabled | Enable Datadog tracing | false |
bifrost.plugins.datadog.version | Plugin config version for DB-backed update tracking (1 to 32767) | 1 |
bifrost.plugins.datadog.config.service_name | Service name | "bifrost" |
bifrost.plugins.datadog.config.agent_addr | Datadog Agent address as combined host:port. Overridden by agent_host | "localhost:8126" |
bifrost.plugins.datadog.config.agent_host | Datadog Agent host, set separately from the port. Supports env.VAR_NAME. Takes precedence over agent_addr | "" |
bifrost.plugins.datadog.config.agent_port | Datadog Agent port, used with agent_host | "8126" |
bifrost.plugins.datadog.config.dogstatsd_addr | DogStatsD address as combined host:port. Overridden by dogstatsd_host | "localhost:8125" |
bifrost.plugins.datadog.config.dogstatsd_host | DogStatsD host, set separately from the port. Supports env.VAR_NAME. Takes precedence over dogstatsd_addr | "" |
bifrost.plugins.datadog.config.dogstatsd_port | DogStatsD port, used with dogstatsd_host | "8125" |
bifrost.plugins.datadog.config.env | Deployment environment tag | "" |
bifrost.plugins.datadog.config.version | Version tag | "" |
bifrost.plugins.datadog.config.enable_traces | Enable trace collection | true |
bifrost.plugins.datadog.config.custom_tags | Extra tags on all spans | {} |
The Datadog Agent is typically deployed via the Datadog Helm chart as a DaemonSet, making it available at the node’s hostIP.Inject the node IP via the downward API and reference it with agent_host / dogstatsd_host. The host comes from the cluster (the node IP) and the port is fixed, so the two can’t be collapsed into one variable — use the separate host/port fields rather than agent_addr.# datadog-values.yaml
image:
tag: "v1.4.11"
bifrost:
plugins:
datadog:
enabled: true
config:
service_name: "bifrost"
agent_host: "env.HOST_IP" # node-local DaemonSet agent (APM traces)
dogstatsd_host: "env.HOST_IP" # node-local DaemonSet agent (metrics)
# agent_port / dogstatsd_port default to 8126 / 8125 — set only for non-standard ports
env: "production"
version: "v1.4.11"
enable_traces: true
custom_tags:
team: "platform"
region: "us-east-1"
# Inject HOST_IP so Bifrost can reach the DaemonSet agent on the same node
env:
- name: HOST_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
Use the env.HOST_IP reference (resolved by Bifrost), not $(HOST_IP). Kubernetes only expands $(VAR) syntax in a container’s env, command, and args — not inside a mounted ConfigMap file, which is where this config lands. Bifrost’s own env. prefix resolves the variable at load time, so it works regardless of where the value is rendered.
helm upgrade bifrost bifrost/bifrost --reuse-values -f datadog-values.yaml
Maxim Observability
Sends LLM request/response data to Maxim for tracing, evaluation, and observability.| Parameter | Description | Default |
|---|
bifrost.plugins.maxim.enabled | Enable Maxim plugin | false |
bifrost.plugins.maxim.version | Plugin config version for DB-backed update tracking (1 to 32767) | 1 |
bifrost.plugins.maxim.config.api_key | Maxim API key (plain text, prefer secret) | "" |
bifrost.plugins.maxim.config.log_repo_id | Maxim log repository ID | "" |
bifrost.plugins.maxim.secretRef.name | Kubernetes Secret name for API key | "" |
bifrost.plugins.maxim.secretRef.key | Key within the secret | "api-key" |
kubectl create secret generic maxim-credentials \
--from-literal=api-key='your-maxim-api-key'
# maxim-values.yaml
image:
tag: "v1.4.11"
bifrost:
plugins:
maxim:
enabled: true
config:
log_repo_id: "your-log-repo-id"
secretRef:
name: "maxim-credentials"
key: "api-key"
helm upgrade bifrost bifrost/bifrost --reuse-values -f maxim-values.yaml
Custom / Dynamic Plugins
Load a custom Go plugin (compiled .so file) at runtime.| Parameter | Description | Default |
|---|
bifrost.plugins.custom[].name | Unique plugin name | "" |
bifrost.plugins.custom[].enabled | Enable custom plugin | false |
bifrost.plugins.custom[].path | Path to compiled .so file in the container | "" |
bifrost.plugins.custom[].version | Plugin config version (1 to 32767) | 1 |
bifrost.plugins.custom[].config | Arbitrary plugin-specific configuration | {} |
bifrost:
plugins:
custom:
- name: "my-custom-plugin"
enabled: true
path: "/plugins/my-plugin.so"
version: 1
config:
api_endpoint: "https://my-service.example.com"
timeout: 5000
Mount the .so file via a volume:volumes:
- name: custom-plugins
configMap:
name: bifrost-custom-plugins
volumeMounts:
- name: custom-plugins
mountPath: /plugins
Or use an init container to download the plugin binary:initContainers:
- name: download-plugin
image: curlimages/curl:8.6.0
command:
- sh
- -c
- |
curl -fsSL https://plugins.example.com/my-plugin.so \
-o /plugins/my-plugin.so
volumeMounts:
- name: plugin-dir
mountPath: /plugins
volumes:
- name: plugin-dir
emptyDir: {}
volumeMounts:
- name: plugin-dir
mountPath: /plugins
helm upgrade bifrost bifrost/bifrost --reuse-values -f custom-plugin-values.yaml
All Plugins Together
# all-plugins-values.yaml
image:
tag: "v1.4.11"
bifrost:
encryptionKeySecret:
name: "bifrost-encryption"
key: "encryption-key"
plugins:
telemetry:
enabled: true
config:
custom_labels:
- name: "environment"
value: "production"
logging:
enabled: true
config:
disable_content_logging: false
logging_headers:
- "x-request-id"
governance:
enabled: true
config:
is_vk_mandatory: true
semanticCache:
enabled: true
config:
provider: "openai"
keys:
- value: "env.CACHE_OPENAI_KEY"
weight: 1
embedding_model: "text-embedding-3-small"
dimension: 1536
threshold: 0.85
ttl: "1h"
otel:
enabled: true
config:
service_name: "bifrost"
collector_url: "otel-collector.observability.svc.cluster.local:4317"
protocol: "grpc"
insecure: true
helm install bifrost bifrost/bifrost -f all-plugins-values.yaml