config/auth/openfga/model.fga; the deployable payload
config/auth/openfga/authorization-model.json is generated from it.
Synopsis
resource:<uuid>#reader@User:<id>.
Fields
Workspace
role
A role is a bag of independent bits. Each enabled bit must be written for both
User:* and Agent:*; a bit written only for User:* denies agent assignees.
role_assignment
Attach each assignment to exactly one object; attaching one assignment to several
objects amplifies its bits across all of them.
project
resource
Agents, skills, MCP servers and registered clients are all
resource:<uuid>.
Which kind a resource is lives in the database, not the graph.
Values
Permission bits
can_read, can_write and can_manage are independent. None implies another. A
role granting only can_write yields can_write: true with can_read: false
and can_manage: false.
Verb to bit — permission service
Applied byrequire_permission and the DI-resolved permission service.
A verb outside this map is denied and logged at warning level.
Verb to bit — relationship explorer
Applied by the/v1/access-control check endpoint. Note the absence of update.
A value that is neither a mapped verb nor one of the three bits returns
allowed: false without querying the graph.
Relation aliases accepted on write
The explorer write endpoint maps a submitted relation onto a resource grant.
Anything else is rejected. A
subject_set (group) body is rejected; bundled
access goes through role and role_assignment.
Namespaces accepted by the explorer
The namespace selects a repository for that check only. The tuple written or
checked is always against
resource:<object>.
Subject forms accepted on write
Seeded tuples
On workspace creation:
On governed resource creation:
The root project id is the workspace id suffixed with
-root. Both writers are
idempotent; an “already exists” response is treated as success.
Enforcement
Invariants the graph cannot express, enforced by the writer instead: one
workspace and one parent per project, one project per resource, same-workspace
edges, an acyclic project graph, one object per
role_assignment, and resource
kind resolved from the database.
Defaults and overrides
ACCESS_CONTROL_BACKEND accepts disabled, keto or openfga.
keto.enabled and openfga.enabled are mutually exclusive; the chart fails
rendering if both are set.
docker-compose.dev.yaml and the Helm chart both set AUTO_BOOTSTRAP and
AUTO_APPLY_MODEL to true. Model paths differ by deployment:
With
AUTO_APPLY_MODEL enabled the file wins and the returned model id is used
for the process lifetime. With it disabled,
ACCESS_CONTROL_OPENFGA_AUTHORIZATION_MODEL_ID is used and the file is ignored.
Bootstrap compares candidate models by normalized content ignoring ids, so an
unchanged model is reused rather than rewritten. When several processes race a
store create, all converge on the earliest store with that name.
Example
Grant a write-only role bundle on a project, and confirm the bits do not roll up:Related
Policy rule syntax
The other authorization surface, which governs tool invocation
Limits
Timeouts and pagination on the graph client
Errors
The 403 and 503 responses these checks produce
The AgentArea model
The reasoning behind the type set