Виртуальные ключи
Виртуальные ключи (Virtual Keys) — основная сущность governance в Meridian. Они дают пользователям и приложениям ограниченный доступ к моделям и провайдерам, а также бюджеты и rate limits.
Обзор
Виртуальные ключи — это основная сущность governance в Meridian. Пользователи и приложения аутентифицируются через специальные заголовки и получают через виртуальный ключ конкретные права доступа, бюджеты и rate limits.
Поддерживаемые заголовки:
x-bf-vk— заголовок виртуального ключа, напримерsk-bf-*;Authorization— заголовок авторизации, напримерBearer sk-bf-*(стиль OpenAI);x-api-key— заголовок API-ключа, напримерsk-bf-*(стиль Anthropic);x-goog-api-key— заголовок API-ключа, напримерsk-bf-*(стиль Google Gemini).
Старые виртуальные ключи (без префикса sk-bf-*) поддерживаются только заголовком x-bf-vk.
Через Authorization, x-api-key и x-goog-api-key можно также передавать прямые ключи провайдера. Подробнее — на странице Управление ключами провайдеров, раздел Direct Key Bypass.
Ключевые возможности:
- Контроль доступа — фильтрация по моделям и провайдерам;
- Управление расходами — независимые бюджеты (учитываются вместе с бюджетами команды или клиента, если они привязаны);
- Rate limits — ограничения по токенам и количеству запросов (только на уровне VK);
- Ограничение по ключам — VK можно ограничить конкретными API-ключами провайдера (тогда VK сможет использовать только их);
- Эксклюзивная привязка — VK принадлежит либо одной команде, либо одному клиенту, либо ничему (взаимоисключающе);
- Активный/неактивный статус — мгновенное включение и отключение доступа.
Конфигурация
- Перейдите в раздел Virtual Keys.
- Нажмите кнопку Add Virtual Key.

Настройки бюджета:
- Max Limit — лимит в долларах (например,
10.50); - Reset Duration —
1m,1h,1d,1w,1M,1Y; - Calendar aligned (опционально) — при включении бюджет сбрасывается по календарным границам в UTC (день/неделя/месяц/год), а не по скользящему окну. Применяется только к периодам день/неделя/месяц/год. См. Бюджеты и лимиты, раздел Calendar aligned budgets.
Rate limits:
- Token Limit — максимум токенов за период;
- Request Limit — максимум запросов за период;
- Reset Duration — частота сброса для каждого лимита.
Привязки:
- Team — привязать к существующей команде (взаимоисключающе с клиентом);
- Customer — привязать к существующему клиенту (взаимоисключающе с командой).
- Нажмите Create Virtual Key.
Создание виртуального ключа (привязан к команде):
curl -X POST http://localhost:8080/api/governance/virtual-keys \
-H "Content-Type: application/json" \
-d '{
"name": "Engineering Team API",
"description": "Main API key for engineering team",
"provider_configs": [
{
"provider": "openai",
"weight": 0.5,
"allowed_models": ["gpt-4o-mini"]
},
{
"provider": "anthropic",
"weight": 0.5,
"allowed_models": ["claude-3-sonnet-20240229"]
}
],
"team_id": "team-eng-001",
"budget": {
"max_limit": 100.00,
"reset_duration": "1M"
},
"rate_limit": {
"token_max_limit": 10000,
"token_reset_duration": "1h",
"request_max_limit": 100,
"request_reset_duration": "1m"
},
"key_ids": ["8c52039e-38c6-48b2-8016-0bd884b7befb"],
"is_active": true
}'Создание виртуального ключа (привязан напрямую к клиенту):
curl -X POST http://localhost:8080/api/governance/virtual-keys \
-H "Content-Type: application/json" \
-d '{
"name": "Executive API Key",
"description": "Direct customer-level API access",
"provider_configs": [
{
"provider": "openai",
"weight": 0.5,
"allowed_models": ["gpt-4o"]
},
{
"provider": "anthropic",
"weight": 0.5,
"allowed_models": ["claude-3-opus-20240229"]
}
],
"customer_id": "customer-acme-corp",
"budget": {
"max_limit": 500.00,
"reset_duration": "1M"
},
"is_active": true
}'team_idиcustomer_idвзаимоисключающи — VK может принадлежать либо одной команде, либо одному клиенту, но не обоим одновременно.key_idsограничивает VK конкретными API-ключами провайдера. Передайте["*"], чтобы разрешить доступ ко всем доступным ключам. Пустой массив[]или отсутствующее поле полностью запрещают доступ к ключам.
Обновление виртуального ключа:
curl -X PUT http://localhost:8080/api/governance/virtual-keys/{vk_id} \
-H "Content-Type: application/json" \
-d '{
"description": "Updated description",
"budget": {
"max_limit": 150.00,
"reset_duration": "1M"
}
}'Получение виртуальных ключей:
# Список всех виртуальных ключей
curl http://localhost:8080/api/governance/virtual-keys
# Получить конкретный виртуальный ключ
curl http://localhost:8080/api/governance/virtual-keys/{vk_id}Удаление виртуального ключа:
curl -X DELETE http://localhost:8080/api/governance/virtual-keys/{vk_id}{
"client": {
"enforce_auth_on_inference": true
},
"governance": {
"virtual_keys": [
{
"id": "vk-001",
"name": "Engineering Team API",
"value": "vk-engineering-main",
"description": "Main API key for engineering team",
"is_active": true,
"provider_configs": [
{
"provider": "openai",
"weight": 0.5,
"allowed_models": ["gpt-4o-mini"]
},
{
"provider": "anthropic",
"weight": 0.5,
"allowed_models": ["claude-3-sonnet-20240229"]
}
],
"team_id": "team-eng-001",
"budget_id": "budget-eng-vk",
"rate_limit_id": "rate-limit-eng-vk",
"keys": [
{"key_id": "8c52039e-38c6-48b2-8016-0bd884b7befb"}
]
},
{
"id": "vk-002",
"name": "Executive API Key",
"value": "vk-executive-direct",
"description": "Direct customer-level API access",
"is_active": true,
"provider_configs": [
{
"provider": "openai",
"weight": 0.5,
"allowed_models": ["gpt-4o"]
},
{
"provider": "anthropic",
"weight": 0.5,
"allowed_models": ["claude-3-opus-20240229"]
}
],
"customer_id": "customer-acme-corp",
"budget_id": "budget-exec-vk",
"keys": [
{"key_id": "8c52039e-38c6-48b2-8016-0bd884b7befb"}
]
}
],
"budgets": [
{
"id": "budget-eng-vk",
"max_limit": 100.00,
"reset_duration": "1M",
"current_usage": 0.0,
"last_reset": "2025-01-01T00:00:00Z"
},
{
"id": "budget-exec-vk",
"max_limit": 500.00,
"reset_duration": "1M",
"current_usage": 0.0,
"last_reset": "2025-01-01T00:00:00Z"
}
],
"rate_limits": [
{
"id": "rate-limit-eng-vk",
"token_max_limit": 10000,
"token_reset_duration": "1h",
"token_current_usage": 0,
"token_last_reset": "2025-01-01T00:00:00Z",
"request_max_limit": 100,
"request_reset_duration": "1m",
"request_current_usage": 0,
"request_last_reset": "2025-01-01T00:00:00Z"
}
]
}
}Группы пользователей
Команды
Команды (Teams) дают организационную группировку для виртуальных ключей и управление бюджетом на уровне отдела. Команда может принадлежать одному клиенту и иметь собственный независимый бюджет.
Ключевые возможности:
- Организационная структура — группировка нескольких виртуальных ключей;
- Независимые бюджеты — управление расходами на уровне отдела (отдельно от бюджета клиента);
- Привязка к клиенту — может принадлежать одному клиенту (опционально);
- Без rate limits — у команды не может быть rate limits (только на уровне VK).
Конфигурация
-
Перейдите в Users & Groups → Teams.
-
Нажмите кнопку Add Team.

Заполните форму и нажмите Create Team.
- Привяжите виртуальные ключи к команде:
- Откройте страницу Virtual Keys;
- отредактируйте виртуальный ключ и привяжите его к команде;
- нажмите Save.
Создание команды:
curl -X POST http://localhost:8080/api/governance/teams \
-H "Content-Type: application/json" \
-d '{
"name": "Engineering Team",
"customer_id": "customer-acme-corp",
"budget": {
"max_limit": 500.00,
"reset_duration": "1M"
}
}'Обновление команды:
curl -X PUT http://localhost:8080/api/governance/teams/{team_id} \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Engineering Team",
"budget": {
"max_limit": 750.00,
"reset_duration": "1M"
}
}'Получение команд:
# Список всех команд
curl http://localhost:8080/api/governance/teams
# Получить конкретную команду
curl http://localhost:8080/api/governance/teams/{team_id}Удаление команды:
curl -X DELETE http://localhost:8080/api/governance/teams/{team_id}{
"governance": {
"teams": [
{
"id": "team-eng-001",
"name": "Engineering Team",
"customer_id": "customer-acme-corp",
"budget_id": "budget-team-eng"
},
{
"id": "team-sales-001",
"name": "Sales Team",
"customer_id": "customer-acme-corp",
"budget_id": "budget-team-sales"
}
],
"budgets": [
{
"id": "budget-team-eng",
"max_limit": 500.00,
"reset_duration": "1M",
"current_usage": 0.0,
"last_reset": "2025-01-01T00:00:00Z"
},
{
"id": "budget-team-sales",
"max_limit": 250.00,
"reset_duration": "1M",
"current_usage": 0.0,
"last_reset": "2025-01-01T00:00:00Z"
}
]
}
}Клиенты
Клиенты (Customers) — это верхний уровень иерархии governance, обычно соответствующий организации или крупному бизнес-подразделению. Они дают контроль бюджета верхнего уровня и общую организационную структуру.
Ключевые возможности:
- Верхний уровень иерархии — самый высокий уровень группировки;
- Независимые бюджеты — управление расходами на уровне организации (отдельно от бюджетов команды и VK);
- Управление командами — содержит несколько команд и прямые VK;
- Без rate limits — у клиента не может быть rate limits (только на уровне VK).
Конфигурация
-
Перейдите в Users & Groups → Customers.
-
Нажмите кнопку Add Customer.

Заполните форму и нажмите Create Customer.
-
Привяжите команды к клиенту:
- Откройте страницу Teams;
- отредактируйте команду и привяжите её к клиенту;
- нажмите Save.
-
Привяжите виртуальные ключи к клиенту:
- Откройте страницу Virtual Keys;
- отредактируйте виртуальный ключ и привяжите его к клиенту;
- нажмите Save.
Создание клиента:
curl -X POST http://localhost:8080/api/governance/customers \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corporation",
"budget": {
"max_limit": 2000.00,
"reset_duration": "1M"
}
}'Обновление клиента:
curl -X PUT http://localhost:8080/api/governance/customers/{customer_id} \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp (Updated)",
"budget": {
"max_limit": 2500.00,
"reset_duration": "1M"
}
}'Получение клиентов:
# Список всех клиентов
curl http://localhost:8080/api/governance/customers
# Получить конкретного клиента
curl http://localhost:8080/api/governance/customers/{customer_id}Удаление клиента:
curl -X DELETE http://localhost:8080/api/governance/customers/{customer_id}{
"governance": {
"customers": [
{
"id": "customer-acme-corp",
"name": "Acme Corporation",
"budget_id": "budget-customer-acme"
},
{
"id": "customer-beta-inc",
"name": "Beta Inc",
"budget_id": "budget-customer-beta"
}
],
"budgets": [
{
"id": "budget-customer-acme",
"max_limit": 2000.00,
"reset_duration": "1M",
"current_usage": 0.0,
"last_reset": "2025-01-01T00:00:00Z"
},
{
"id": "budget-customer-beta",
"max_limit": 1500.00,
"reset_duration": "1M",
"current_usage": 0.0,
"last_reset": "2025-01-01T00:00:00Z"
}
]
}
}Связанные возможности
- Бюджеты и лимиты — enterprise-уровень управления бюджетами, контроля расходов и rate limits через виртуальные ключи.
- Маршрутизация — направление запросов к нужным провайдерам/моделям и ограничение API-ключей через виртуальные ключи.
- Фильтрация MCP-инструментов — управление MCP-клиентами и инструментами для виртуальных ключей.
Использование
Сделать виртуальные ключи обязательными
Все запросы с включённым governance должны передавать заголовок виртуального ключа:
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-bf-vk: vk-engineering-main" \
-d '{
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Hello!"}]
}'По умолчанию governance необязателен: если заголовок виртуального ключа отсутствует, запрос всё равно проходит — но без проверок и маршрутизации governance. Это поведение можно сделать строгим, потребовав обязательного заголовка виртуального ключа.
-
Перейдите в Config → Security.
-
Включите чекбокс Enforce Virtual Keys.

curl -X PUT http://localhost:8080/api/config \
-H "Content-Type: application/json" \
-d '{
"client_config": {
"enforce_auth_on_inference": true
}
}'{
"client": {
"enforce_auth_on_inference": true
}
}При включённом обязательном использовании заголовка governance запрос отклоняется, если x-bf-vk не передан.
Аутентификация и виртуальные ключи
Виртуальные ключи и HTTP-аутентификация — это независимые слои, которые могут работать совместно:
| Слой | Назначение | Заголовки |
|---|---|---|
| Аутентификация | Проверка личности пользователя | Authorization: Basic/Bearer <credentials> |
| Виртуальные ключи | Маршрутизация и governance запроса | x-bf-vk, Authorization1, x-api-key, x-goog-api-key |
Когда disable_auth_on_inference: true (аутентификация отключена):
Виртуальные ключи можно передавать через любой поддерживаемый заголовок без дополнительной аутентификации:
# Через заголовок x-bf-vk
curl -X POST http://localhost:8080/v1/chat/completions \
-H "x-bf-vk: <VIRTUAL_KEY>" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o-mini", "messages": [...]}'
# Через заголовок Authorization (стиль OpenAI)
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer <VIRTUAL_KEY>" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o-mini", "messages": [...]}'Когда disable_auth_on_inference: false (аутентификация включена):
Нужно передать и учётные данные аутентификации, и виртуальный ключ. Виртуальный ключ — в x-bf-vk, потому что Authorization уже занят аутентификацией:
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Basic <base64-credentials>" \
-H "x-bf-vk: <VIRTUAL_KEY>" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o-mini", "messages": [...]}'Настройка disable_auth_on_inference:
- Перейдите в Config → Security.
- Переключите Disable Auth on Inference, чтобы включить или отключить.

curl -X PUT http://localhost:8080/api/config \
-H "Content-Type: application/json" \
-d '{
"auth_config": {
"disable_auth_on_inference": true
}
}'{
"auth_config": {
"is_enabled": true,
"disable_auth_on_inference": true
}
}Ошибки
- Виртуальный ключ не найден (400):
{
"error": {
"type": "virtual_key_required",
"message": "virtual key is missing in headers"
}
}- Виртуальный ключ заблокирован (403):
{
"error": {
"type": "virtual_key_blocked",
"message": "Virtual key is inactive"
}
}- Превышен rate limit (429):
{
"error": {
"type": "rate_limited",
"message": "Rate limits exceeded: [token limit exceeded (1500/1000, resets every 1h)]"
}
}- Превышен лимит токенов (429):
{
"error": {
"type": "token_limited",
"message": "Rate limits exceeded: [token limit exceeded (1500/1000, resets every 1h)]"
}
}- Превышен лимит запросов (429):
{
"error": {
"type": "request_limited",
"message": "Rate limits exceeded: [request limit exceeded (101/100, resets every 1m)]"
}
}- Превышен бюджет (402):
{
"error": {
"type": "budget_exceeded",
"message": "Budget exceeded: VK budget exceeded: 105.50 > 100.00 dollars"
}
}- Модель не разрешена (403):
{
"error": {
"type": "model_blocked",
"message": "Model 'gpt-4o' is not allowed for this virtual key"
}
}- Провайдер не разрешён (403):
{
"error": {
"type": "provider_blocked",
"message": "Provider 'anthropic' is not allowed for this virtual key"
}
}Footnotes
-
Authorizationможет нести виртуальный ключ только при отключённой аутентификации (disable_auth_on_inference: true). Когда аутентификация включена, заголовокAuthorizationпотребляется ею и не может использоваться для виртуальных ключей. ↩
Асинхронный inference
Отправка inference-запросов асинхронно с последующим получением результата по job_id.
Маршрутизация
Направление запросов к конкретным моделям, провайдерам и API-ключам с помощью виртуальных ключей. Балансировка нагрузки по весам, автоматические fallback'и и тонкий контроль доступа.