Skip to content

Resource Groups

A resource group (RG) is the unit of governance in Brutor. Everything the gateway enforces — which models a request may use, which MCP tools it may call, which guardrails run, how much it may spend — is resolved through the resource group the request executes in.

You arrange resource groups in a hierarchy that mirrors your organization, and Brutor resolves access and limits by walking that tree.

Acme Corp (organization)
├── Engineering (department)
│ ├── Platform Team (team)
│ └── Coding Agents (agent)
└── Sales (department)
└── Revenue Sentinel (agent)

Each group has a group_type that is purely descriptive (it does not change behavior): organization, division, department, business_unit, team, project, program, initiative, application, agent, assistant, service, or custom.

A resource group is a hub. You attach resources, people, and controls to it:

Binding What it does Where
LLM models Grants access to a model (or routing-group virtual model) POST /v1/admin/resource-groups/{id}/llm-models
MCP servers Grants access to a registered MCP server’s tools POST /v1/admin/resource-groups/{id}/server-configs
Agent skills Grants access to skills (skill access control is RG-only) Admin UI → group → Agent Skills
Knowledge bases KB collections and connectors available to the group GET .../effective-kb-collections, .../effective-kb-connectors
A2A agent cards Which published agents the group may call PUT /v1/admin/resource-groups/{id}/agent-cards
End-user groups Puts people in the group (via their user groups) POST /v1/admin/resource-groups/{id}/end-user-groups
Direct members Individual end users added directly POST /v1/admin/resource-groups/{id}/members
API keys Shared keys bound to this group for programmatic access POST /v1/admin/resource-groups/{id}/api-keys
Guardrail configs Guardrails apply to groups listed in the config’s group_ids See Guardrails
Semantic cache configs Response caching scoped to the group POST /v1/admin/semantic-cache/configs/{id}/groups
Policies Argument & semantic policies bound via group_ids / resource_group_ids See Policies
Limits & governance Budgets, quotas, rate limits, parameter clamps See Budgets, quotas & rate limits
Usage alerts Threshold alerts on the group’s consumption See Usage alerts

At runtime, every request through the Core Proxy executes in exactly one active resource group (chosen from the caller’s memberships — see how the active group is picked), and the group’s effective resources, guardrails, and limits apply.

Resource group with AI models bound to it

This is the part to get right. Inheritance treats resources and restrictions differently:

  1. Resources are additive down the tree, gated by inherit_resources. A child group with inherit_resources: true sees everything bound to its ancestors plus its own direct bindings. Set inherit_resources: false on a group and inheritance stops there: the group (and, through it, its subtree) sees only what is bound at or below that point.

  2. Limits and policies are always restrictive — never widened by inheritance. A parent’s budget, rate limit, parameter clamp, or policy applies to the whole subtree regardless of any flag. A child can add tighter limits of its own; it can never loosen what an ancestor imposed. Effective numeric limits resolve to the minimum across the ancestor chain.

  3. Memberships and API keys are NOT inherited. Being a member of Engineering does not make you a member of Platform Team, and an API key bound to a parent does not work as a key for a child. People and keys belong to exactly the groups they are explicitly attached to.

The Admin UI and the API both expose the effective view — direct plus inherited — so you can verify what a group actually resolves to:

Terminal window
curl http://localhost:5050/v1/admin/resource-groups/{group_id}/effective-llm-models \
-H "Authorization: Bearer $ADMIN_TOKEN"

Equivalent endpoints exist for the other resource types: effective-server-configs, effective-agent-skills, effective-guardrails, effective-kb-collections, effective-kb-connectors, effective-agent-cards.

Each (resource group, model) binding row carries a portal_visible flag. It controls only whether the model shows up in the User Portal’s model picker — it never affects whether the model can be called.

Defaults are smart: embedding models default hidden (you can’t chat with one), and a routing member defaults hidden when its routing group is bound to the same RG (users should pick the virtual routing model, not the members). Everything else defaults visible. You can override explicitly:

Terminal window
curl -X PATCH \
http://localhost:5050/v1/admin/resource-groups/{group_id}/llm-models/{model_id}/portal-visibility \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"portal_visible": false}'

Inherited models carry the flag from whichever ancestor binds them directly; a direct binding on the child overrides the ancestor’s setting.

A routing group (load-balancing / fallback across member models) is itself addressable as a virtual model — but binding it to a resource group grants nothing on its own, and binding only the members doesn’t expose the virtual model either.

Bind both: the routing group’s virtual model and every member model must be bound to the RG for routing to work. The same rule applies whether callers authenticate with a portal JWT or an API key.

Terminal window
# Find the routing group's virtual model
curl "http://localhost:5050/v1/admin/llms?mode=routing" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Bind virtual model AND each member model to the group
curl -X POST http://localhost:5050/v1/admin/resource-groups/{group_id}/llm-models \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"llm_model_id": "<virtual-or-member-model-id>"}'
  1. Go to Resource Groups and create a group — pick a name, a type, and (optionally) a parent group. Leave Inherit resources from parent on unless this subtree must be isolated.

  2. Open the group and work through its tabs: AI Models, MCP Servers, Agent Skills to bind resources; Members to attach end-user groups or individual users; API Keys to mint programmatic access.

  3. Set restrictions on the LLM Limits / MCP Limits tabs and watch consumption on Usage.

Members tab of a resource group

Everything the UI does is the Admin API on the Control Plane (:5050). Create parents before children:

Terminal window
curl -X POST http://localhost:5050/v1/admin/resource-groups \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "platform-team",
"display_name": "Platform Team",
"description": "Internal platform engineering",
"group_type": "team",
"parent_group_id": "<engineering-group-id>",
"inherit_resources": true
}'
{
"id": "9f2c...",
"name": "platform-team",
"display_name": "Platform Team",
"group_type": "team",
"inherit_resources": true
}

Then bind an end-user group and a model:

Terminal window
curl -X POST http://localhost:5050/v1/admin/resource-groups/9f2c.../end-user-groups \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"end_user_group_ids": ["<end-user-group-id>"]}'
curl -X POST http://localhost:5050/v1/admin/resource-groups/9f2c.../llm-models \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"llm_model_id": "<model-id>"}'

Binding POSTs are idempotent-friendly: a repeat returns 409 Conflict, which provisioning scripts treat as “already there”. See Provision with a setup script for the full pattern.

Hierarchy inspection endpoints: GET /v1/admin/resource-groups/{id}/ancestors, /descendants, /children, /siblings, and GET /v1/admin/resource-groups/hierarchy-tree for the whole forest.