Команды и доступ
🔵 Команда — разделяемый между пользователями субъект: общий team:<id>
state и права на агентов, инструменты и роуты. Членство — «многие-ко-многим»:
пользователь может состоять в нескольких командах, а команда — иметь много
участников. Фича выключена по умолчанию и включается наличием реестра команд.
45.1 Модель
Команда добавляет четвёртый вид scope к scoped state store
(session / user / agent / team):
enum Scope { Session(String), User(String), Agent(String), Team(String) }
- ключ
team:<id>— общий для всех участников команды; - скалярный резолв
get_resolvedидётuser → session → team(s) → agent: личное перекрывает командное, командное — шаблон агента. Команды разворачиваются из членств пользователя (отсортированы по id); - команда адресуется явно (
team:<id>) там, где нужен конкретный id.
Членство и гранты хранит 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
- команды, членства и гранты — бессрочны (по возрасту не чистятся);
- очистка state — только по per-элементному
ttl_secs(периодическийpurge_expired, ежечасно); DELETE /v1/teams/:idкаскадно удаляет участников, гранты и весьteam:<id>state.