Документация

Строгая мультитенантность

Как betool изолирует ваши данные, секреты и исполнения от других организаций.

Строгая мультитенантность

Изоляция между организациями — основополагающий инвариант betool. Вот как она обеспечивается, слой за слоем.

На уровне базы данных

Бизнес-таблицы содержат столбец org_id, который привязывает каждую строку к её организации (чаще всего с ограничением внешнего ключа на organisation(id)).

Все серверные запросы проходят через методы хранилища (store), которые систематически фильтруют по org_id текущей сессии. Ни один путь исполнения кода не предоставляет произвольный запрос; ни один клиент не может навязать другой org_id.

Для редких ресурсов, допускающих совместное использование (как правило, общие учётные записи LLM на уровне платформы), столбец is_shared BOOLEAN NOT NULL DEFAULT FALSE включает явное согласие (opt-in). Правила:

  • Любое чтение разрешено с WHERE org_id = %s OR is_shared = TRUE.
  • Любое изменение строки с is_shared = TRUE требует роли hyperadmin (оператора платформы).
  • Любое изменение инвалидирует кэш глобально — без расхождений по организациям.

На уровне хранилища файлов

Файлы размещаются в объектном хранилище (локальный диск или облачный bucket в зависимости от развёртывания), в пространстве, принадлежащем каждой организации:

orgs/
└── <org_id>/
    ├── files/<canal>/<exchange_id>/<fichier>   ← fichiers reçus par les canaux
    └── fixtures/                               ← fixtures de benchmark

Ключи формируются на стороне сервера (очищенное имя файла, защита от path traversal). Любое чтение файла от имени пользователя, инструмента или агента проходит проверку, которая отклоняет ключ вне пространства orgs/<org_id>/files/ текущей организации.

На уровне кэша

Кэши приложения (например, разрешение провайдеров LLM) индексируются по организации. При изменении ресурса с is_shared = TRUE кэш инвалидируется глобально — нет окна, в течение которого организация могла бы увидеть устаревшее значение.

На уровне секретов

Учётные данные (ключи LLM, токены аутентификации операторов, секреты webhook) хранятся на стороне сервера и привязаны к каждой организации. Они никогда не возвращаются в открытом виде API: маршруты GET указывают только на их наличие (например, has_api_key: bool).

На уровне исполнения

При запуске pipeline:

  • Операторы имеют доступ только к внешним учётным записям, объявленным в организации.
  • Модели LLM разрешаются в порядке own > shared (автоматический fallback) — всегда.
  • Прикреплённые файлы читаемы только в пространстве хранилища текущей организации.

Если оператор пытается получить доступ к ресурсу вне периметра, доступ отклоняется с явной ошибкой — а не молчаливым таймаутом.

Практический тест

При каждом добавлении нового совместно используемого ресурса команда отвечает на 4 вопроса:

  1. Если появится вторая организация backoffice, будет ли мой код вести себя корректно?
  2. Если я изменяю общую строку, инвалидируются ли все кэши по организациям?
  3. Остаются ли мои секреты строго на стороне сервера?
  4. Позволяет ли мой GET picker-ам других организаций перечислить ресурс, не раскрывая секрет?

Если хотя бы один ответ отрицательный, ресурс не выпускается.