Команды и доступ

🔵 Команда — разделяемый между пользователями субъект: общий team:<id> state и права на агентов, инструменты и роуты. Членство — «многие-ко-многим»: пользователь может состоять в нескольких командах, а команда — иметь много участников. Фича выключена по умолчанию и включается наличием реестра команд.


45.1 Модель

Команда добавляет четвёртый вид scope к scoped state store (session / user / agent / team):

enum Scope { Session(String), User(String), Agent(String), Team(String) }

Членство и гранты хранит TeamRegistry (logs/teams.db, DuckDB). Это источник истины для прав; при отсутствии реестра команды «fail closed».

Сущность Роль
TeamRole::Member читает/пишет state команды
TeamRole::Owner то же + управляет составом и грантами
GrantKind Что выдаёт
template доступ к шаблону агента
tool доступ к team-gated инструменту
route доступ к модульному роуту Access::Team
admin админ-доступ к конкретному шаблону (только оператор)

Эскалации нет by construction. Owner может выдать другой команде лишь то, что уже есть у него самого; admin-грант выдаёт только оператор, и он наследуется только owner'ами.


45.2 Включение

logging:
  team_db_path: "logs/teams.db"   # пусто = команды выключены

teams:
  # Инструменты, доступные только по гранту команды.
  gated_tools: []

auth:
  oidc:
    providers:
      yandex:
        userinfo_groups_field: "groups"   # claim со списком команд
    team_owner_groups: ["admins"]         # эти группы → роль owner
Настройка По умолчанию Смысл
logging.team_db_path logs/teams.db реестр команд; пусто = фича выключена
teams.gated_tools [] список team-gated инструментов
auth.oidc.providers.<p>.userinfo_groups_field (пусто) поле userinfo со списком групп
auth.oidc.team_owner_groups [] группы, дающие роль owner при синке

45.3 Управление: admin API и owner API

Админский API (операторская аутентификация, полный доступ, admin-гранты разрешены):

Метод и путь Действие
GET /v1/teams список команд
POST /v1/teams создать/обновить {id, name?}
GET /v1/teams/:id команда + участники + гранты
DELETE /v1/teams/:id удалить команду (каскад: участники, гранты и team:<id> state)
POST /v1/teams/:id/members добавить участника {user_id, role?, source?}
DELETE /v1/teams/:id/members/:user убрать участника
POST /v1/teams/:id/grants выдать грант {kind, resource}
DELETE /v1/teams/:id/grants/:kind/:resource снять грант

Owner API (end-user аутентификация) — owner управляет только своей командой и только в пределах своих грантов (admin-гранты запрещены):

Метод и путь Действие
POST /v1/my/teams/:id/members добавить участника
DELETE /v1/my/teams/:id/members/:user убрать участника
POST /v1/my/teams/:id/grants выдать грант (только из своих)
DELETE /v1/my/teams/:id/grants/:kind/:resource снять грант

OIDC-синк. При входе end-пользователя через OIDC группы из userinfo_groups_field аддитивно добавляются как членства (source = oidc; группа из team_owner_groups → owner, иначе member). Синк ничего не удаляет, поэтому ручные гранты не затираются.


45.4 Что именно энфорсится

Сущность Правило
State сисколлы Set/Get/DeleteState разрешают team:<id> только участнику команды
Агенты пользователь, состоящий в командах, может чатиться с шаблоном только если он выдан одной из его команд и пройдено per-agent login-требование
Инструменты инструмент из teams.gated_tools требует tool-грант
Роуты модульный роут Access::Team требует route-грант на путь роута

Обратная совместимость: без реестра команд, при анонимном запросе или для пользователя, не состоящего ни в одной команде, поведение не меняется.


45.5 Retention