botruntime
Основы платформы

Воркспейсы, участники и токены

Как устроена тенантность в облаке botruntime: воркспейс как граница доступа, роли участников и виды токенов.

Воркспейс — это граница тенантности. Всё, что создаёт бот, живёт внутри воркспейса: диалоги, сообщения, события, файлы, строки таблиц, метеринг. Воркспейс платформа выводит из токена, а не из заголовков запроса. Рядом в том же воркспейсе — боты, участники с ролями и выпущенные ключи.

Тенант не выбирается клиентом. Переключиться на чужой воркспейс, подставив чужой идентификатор в путь, тело или заголовок, нельзя.

Отсюда мультитенантность из коробки. Облако обслуживает много воркспейсов сразу, и код доступа к данным не пересекает их границу: scope приходит из аутентификации, а не из ввода.

Воркспейс как граница

Каждый запрос к API проходит один из двух швов аутентификации. Оба резолвят Authorization: Bearer … в scope тенанта: тройку (воркспейс, владелец, бот). Дальше все обращения к БД фильтруются этим scope.

Главный инвариант: тенант берётся из ключа, никогда из клиентских заголовков. Заголовки x-workspace-id или x-bot-id лишь выбирают сущность среди доступных ключу. Принадлежность перепроверяется по членству в базе. Подставили чужой workspaceId — получите 404, а не доступ. Существование чужого тенанта платформа не раскрывает.

Анонимного доступа нет. Отсутствующий, неверный или отозванный токен — всегда 401. Платформа никогда молча не подставляет «владельца по умолчанию». Сбой инфраструктуры (например, недоступная БД при проверке ключа) отдаётся как 5xx, а не маскируется под 401.

Участники и роли

Участник — это человек-аккаунт в воркспейсе с одной из трёх ролей. Роль платформа резолвит по членству и отдаёт в GET /v1/admin/me вместе со списком воркспейсов.

РольНазначение
ownerСоздатель воркспейса. Единственный управляет составом участников. Строка владельца неизменяема.
adminПишет конфигурацию: интеграции, config-переменные, строки таблиц; читает аудит.
memberЧитает данные бота: диалоги, события, файлы, таблицы, логи, аналитику.

Матрица прав проверяется на сервере — до хендлера или внутри него.

Действиеmemberadminowner
Читать диалоги, сообщения, события, трейсы, файлы, таблицы, логи, аналитику
Читать имена config-переменных (без значений)
Читать журнал аудита воркспейса
Ставить, снимать и подключать интеграции
Писать и удалять config-переменные (секреты)
Вставлять и удалять строки таблиц из консоли
Добавлять, менять роль и удалять участников

Роль owner неотчуждаема: её нельзя понизить, а самого владельца — удалить из воркспейса. Так воркспейс никогда не остаётся без владельца. Добавить участника можно только по e-mail уже вошедшего в платформу человека — приглашений «в никуда» по непроверенному адресу нет.

Создать воркспейс может любой аккаунт: POST /v1/admin/workspaces. Создатель сразу становится owner.

Две оси доступа

Аутентификация идёт по двум параллельным осям. Они не пересекаются и не подменяют друг друга.

  • Человеческий вход. Человек входит в консоль через внешний OIDC-провайдер. Консоль получает session-JWT и ходит на human-дерево API (/v1/admin/me, /v1/admin/workspaces/*). Всё скоупится членством человека в воркспейсах.
  • Машинный доступ. CLI brt, боты и сервисы предъявляют api-ключ или личный токен. Это отдельная ветка middleware, scope выводится из самого ключа.

Ветку платформа выбирает по форме токена, без угадывания. Префикс brt_pat_ — это PAT-путь. Всё прочее без префикса идёт на верификацию session-JWT. Двусмысленности нет: session-JWT это compact JWS, он начинается с eyJ и префикс нести не может.

Таксономия токенов

ТокенФормаНосительScopeГде выпускается
Сессия консолиcompact JWS (eyJ…)человекаккаунт → его воркспейсы и роливнешний OIDC-провайдер при входе в консоль
Личный токен (PAT)brt_pat_…человек-аккаунтна human-дереве — как владелец аккаунта; на машинной шине с x-workspace-id — воркспейсстраница «Аккаунт» в консоли или профиль brt
Ключ ботаbf_…конкретный бот(воркспейс, владелец, бот)provision при первом деплое или POST /v1/admin/bots/baks
Ключ интеграцииbf_…интеграцияворкспейс, BotID=0POST /v1/admin/integrations/iaks

Все токены хранятся в базе только как sha256-хеш. Сырое значение ключа или PAT показывается ровно один раз, в ответе на создание, и невосстановимо. Потеряли — выпускайте новый и отзывайте старый. Мягкий отзыв виден следующему же запросу.

Секреты не передаются через аргументы командной строки. В brt значения задаются только через stdin или файл:

terminal
printf '%s' "$MY_SECRET" | brt secret set STRIPE_KEY

Так секрет не оседает в истории shell, в списке процессов и в логах.

Как резолвится тенант из токена

  • Ключ бота (bf_, per-bot) несёт ненулевой BotID и работает на всех маршрутах, включая привязанные к конкретному боту.
  • Личный токен (brt_pat_) на human-дереве действует как аккаунт-владелец. На машинной шине с заголовком x-workspace-id становится воркспейс-скоупным принципалом (BotID=0). Заголовок лишь выбирает воркспейс среди тех, где аккаунт состоит; членство перепроверяется — не член получает 403, отсутствие заголовка — 400.
  • Ключ интеграции (bf_, iak) воркспейс-скоупный и несёт BotID=0. Он работает на воркспейс-широких поверхностях (реестр, определения интеграций), но на любом маршруте конкретного бота получает честный 403. Ключ интеграции не может «просочиться» под видом ключа бота.

403 this API key has no bot scope — преднамеренный гейт на краю API, а не побочный сбой. Ключ без bot-scope режется до хендлера, а не где-то в глубине data-plane.

Проверка через API

Личный токен на human-дереве возвращает ваш аккаунт и все воркспейсы с ролями.

terminal
curl https://botruntime.ru/v1/admin/me \
  -H "Authorization: Bearer brt_pat_2f6b…c91a"
ответ
{
  "account": {
    "id": "42",
    "email": "ops@example.com"
  },
  "workspaces": [
    { "id": "7", "name": "Acme", "handle": "acme", "role": "owner" },
    { "id": "9", "name": "Beta", "handle": "beta", "role": "member" }
  ]
}

Тот же PAT на машинной шине требует x-workspace-id, чтобы выбрать воркспейс из списка:

terminal
curl "https://botruntime.ru/v1/admin/workspaces/7/usages?type=ai_spend" \
  -H "Authorization: Bearer brt_pat_2f6b…c91a" \
  -H "x-workspace-id: 7"

Метеринг и квоты

Платформа измеряет потребление на уровне воркспейса. Платёжного движка в ней нет: учёт есть, биллинг — вне области платформы.

  • Метеринг. Расход пишется в append-only леджер meter_event. Основной учитываемый юнит — llm_cost_micro_usd: стоимость LLM в микро-долларах, целочисленные деньги, без float. Расход агрегируется по месяцам и разбивается по ботам (GET /v1/admin/usages/…, /workspaces/{id}/usages/by-bot).
  • Квоты. У воркспейса есть лимиты — например, ai_spend, потолок трат на LLM в USD/месяц. Дефолты консервативны; override пишется через PUT /v1/admin/quotas/ai-spend.

Не привязывайте стоимость к отдельному ответу бота. Расход учитывается леджером агрегатов (usages), а не как цена за реплику. Хранилище файлов считается как текущий STOCK (сумма размеров), а не по записанным байтам.

У ботов нет собственных ключей LLM-провайдера: модельные вызовы идут через шлюз платформы, где и живут лимиты, метеринг и трейсинг. Трейсы всегда metadata-only: содержимое сообщений, prompt/output и user/session/message IDs не записываются и не экспортируются.

Дальше

On this page