gram-rbac

Installation
SKILL.md

Gram's RBAC is a scope-and-selector model. The server ships with a fixed set of scopes grouped into system roles (admin, member). A grant binds a scope to a selector (a Kubernetes-style map[string]string of resource_kind, resource_id, plus optional narrowing dimensions like tool or disposition) for a given principal (user or custom role). Handlers enforce scopes by calling authz.Engine.Require(ctx, authz.Check{...}); the dashboard renders the same scope vocabulary through a matching TypeScript union that is hand-maintained in lockstep with the server.

Concepts and terminology

Scope. A named permission that authorizes an operation on a particular kind of resource.

Resource type. The kind of resource a scope protects — currently org, project, or mcp. Every scope has exactly one resource type.

Scope expansion. Higher-privilege scopes satisfy lower-privilege ones. In the read/write/connect family the privilege order is write > read > connect: mcp:write satisfies a mcp:read check, and either mcp:read or mcp:write satisfies a mcp:connect check (connect is the broadest, easiest-to-satisfy gate). The mapping lives in scopeExpansions in authz/scopes.go — key = required scope, value = higher-privilege scopes that also satisfy it.

Selector. A map[string]string of constraints attached to a grant or check. Always carries resource_kind and resource_id (both required); MCP scopes additionally allow tool and disposition. Wildcards are explicit values — {"resource_kind":"*","resource_id":"*"}, never empty {}. Defined in server/internal/authz/selector.go.

Selector matching. A grant selector satisfies a check selector when, for every key the grant constrains, either the values are equal or the grant value is "*". Keys present on the grant but absent from the check are skipped — this is what lets a disposition-scoped grant ({"disposition":"read_only"}) still satisfy a connection-level check that doesn't constrain disposition.

Grant. A tuple of {Scope, Selector} held by a principal. The API-visible forms are RoleGrant (carrying Selectors []Selector) and ListRoleGrant (which also carries the transitively-implied sub_scopes). Use authz.NewGrant(scope, resourceID) to construct one — it derives the selector's resource_kind from the scope family.

Principal. Who holds a grant — a urn.Principal with a type (user, role, service account) and an id.

Dimensions. Optional narrowing keys on a Check beyond resource_id. Today: tool and disposition for MCP scopes (see server/internal/authz/checks.go and MCPToolCallCheck). Allowed keys per scope family are enforced by ValidateSelector; new dimensions must be added to allowedSelectorKeys in selector.go.

Installs
1
GitHub Stars
219
First Seen
May 16, 2026
gram-rbac — speakeasy-api/gram