Commands and access
🔵 Team — an entity shared between users: shared team:<id>
state and rights to agents, tools, and routes. Membership is "many-to-many":
a user can belong to several teams, and a team can have many
members. The feature is disabled by default and is enabled by the presence of a team registry.
45.1 Model
A team adds a fourth kind of scope to the scoped state store
(session / user / agent / team):
enum Scope { Session(String), User(String), Agent(String), Team(String) }
- the
team:<id>key is shared by all team members; - scalar resolution of
get_resolvedproceedsuser → session → team(s) → agent: personal overrides team, team overrides the agent template. Teams are expanded from the user's memberships (sorted by id); - a team is addressed explicitly (
team:<id>) where a specific id is needed.
Membership and grants are stored by TeamRegistry (logs/teams.db, DuckDB). This is
the source of truth for rights; in the absence of a registry, teams "fail closed".
| Entity | Role |
|---|---|
TeamRole::Member |
reads/writes the team's state |
TeamRole::Owner |
the same + manages membership and grants |
GrantKind |
What it grants |
|---|---|
template |
access to the agent template |
tool |
access to a team-gated tool |
route |
access to the modular route Access::Team |
admin |
admin access to a specific template (operator only) |
There is no escalation by construction. An owner can grant another team only what he himself already has; the
admingrant is issued only by an operator, and it is inherited only by owners.
45.2 Enabling
logging:
team_db_path: "logs/teams.db" # пусто = команды выключены
teams:
# Инструменты, доступные только по гранту команды.
gated_tools: []
auth:
oidc:
providers:
yandex:
userinfo_groups_field: "groups" # claim со списком команд
team_owner_groups: ["admins"] # эти группы → роль owner
| Setting | Default | Meaning |
|---|---|---|
logging.team_db_path |
logs/teams.db |
team registry; empty = feature disabled |
teams.gated_tools |
[] |
list of team-gated tools |
auth.oidc.providers.<p>.userinfo_groups_field |
(empty) | userinfo field with the list of groups |
auth.oidc.team_owner_groups |
[] |
groups that grant the owner role on sync |
45.3 Management: admin API and owner API
Admin API (operator authentication, full access, admin grants
allowed):
| Method and path | Action |
|---|---|
GET /v1/teams |
list teams |
POST /v1/teams |
create/update {id, name?} |
GET /v1/teams/:id |
team + members + grants |
DELETE /v1/teams/:id |
delete a team (cascade: members, grants, and team:<id> state) |
POST /v1/teams/:id/members |
add member {user_id, role?, source?} |
DELETE /v1/teams/:id/members/:user |
remove member |
POST /v1/teams/:id/grants |
issue grant {kind, resource} |
DELETE /v1/teams/:id/grants/:kind/:resource |
revoke grant |
Owner API (end-user authentication) — an owner manages only his own
team and only within the limits of his grants (admin grants are forbidden):
| Method and path | Action |
|---|---|
POST /v1/my/teams/:id/members |
add member |
DELETE /v1/my/teams/:id/members/:user |
remove member |
POST /v1/my/teams/:id/grants |
issue grant (only from his own) |
DELETE /v1/my/teams/:id/grants/:kind/:resource |
revoke grant |
OIDC sync. When an end user logs in via OIDC, groups from
userinfo_groups_field are additively added as memberships (source = oidc;
a group from team_owner_groups → owner, otherwise member). The sync deletes nothing,
so manual grants are not overwritten.
45.4 What exactly is enforced
| Entity | Rule |
|---|---|
| State | syscalls Set/Get/DeleteState allow team:<id> only to a team member |
| Agents | a user who belongs to teams can chat with a template only if it is granted to one of his teams and the per-agent login requirement is met |
| Tools | a tool from teams.gated_tools requires a tool grant |
| Routes | the modular route Access::Team requires a route grant for the route path |
Backward compatibility: without a team registry, for an anonymous request, or for a user who belongs to no team, the behavior does not change.
45.5 Retention
- teams, memberships, and grants are perpetual (not cleaned up by age);
- state cleanup is only per-element
ttl_secs(periodicpurge_expired, hourly); DELETE /v1/teams/:idcascades the deletion of members, grants, and allteam:<id>state.