In the DSL there are two kinds of words: keywords you must type exactly, and
names you make up (or point to). Symbols like : and [ ] are also fixed.
Built-in words. Spell them exactly.
| Keyword | What it does |
|---|---|
model | Starts the file. |
schema | Comes with the version, like schema 1.1. |
type | Starts a new type. |
relations | Starts the list of relations for a type. |
define | Starts one relation. |
or, and, but not | Combine relations. |
from | Inherit through a link. |
Words you invent to fit your app.
user, document, folder, organization. You could call them person or file instead.owner, editor, viewer, parent, member. You pick these too.There is a small twist. Sometimes you create a name, and sometimes you refer to a name you already made.
define viewer: … → here you create the name viewer.… or owner → here you refer to owner, which must already exist.[user] → user must be a type you already defined.: after a relation name — required.[ ] around allowed types — required for a direct relation.# — used in tuples, like group:eng#member (not in the model itself).Take this line:
define viewer: [user] or owner
| Token | Kind | Note |
|---|---|---|
define | keyword | fixed word |
viewer | name | you create it here |
: | symbol | required |
[ | symbol | required |
user | name | a type that must already exist |
] | symbol | required |
or | keyword | fixed word |
owner | name | a relation that must already exist |
Uppercase = keywords (fixed). <…> = names you supply.
MODEL
SCHEMA 1.1
TYPE <your type name>
RELATIONS
DEFINE <your relation name>: [<allowed type>] OR <another relation>
Each keyword plays one clear role. Think of building the model top to bottom: first the file, then a type, then its relations, then each rule.
Concept: the start of the whole file. It wraps everything.
You write it once, at the very top. Nothing lives outside it.
model schema 1.1
Concept: the DSL version number. It tells OpenFGA which grammar to use.
Always pair it with a version, like 1.1. Put it right under model.
schema 1.1
Concept: a kind of thing in your system.
A type is a category of object, like a user, a document, or a folder. You will
have one type block for each kind of thing. The name after type is yours to
choose.
type document
Read it as: “documents are a kind of thing in this system.”
Concept: the heading that opens the list of links for a type.
It has no rule of its own. It just says “the rules for this type start here.” Everything indented under it belongs to that type.
type document
relations
define owner: [user]
define viewer: [user] or owner
Read it as: “here come the relations that documents can have.”
Concept: one single rule (one relation).
Each define line creates one named link and says who can have it. You will have
many define lines under relations.
define owner: [user]
Read it as: “define a link called owner; a user can be assigned to it.”
Concept: ways to combine relations into one rule.
or — this or that (widest access).and — this and that (both required).but not — this except that (takes access away).define editor: [user] or owner define can_delete: owner and admin define viewer: [user] but not blocked
Concept: inherit access through a link to another object.
It lets access flow between objects. You name a link to follow, then a relation to check on the other side.
define viewer: viewer from parent
Read it as: “you are a viewer here if you are a viewer of the parent.”
The keywords stack inside each other, shown by indentation:
model ← the whole file
schema 1.1 ← the DSL version
type document ← a kind of thing
relations ← its links start here
define owner: [user] ← one rule
define editor: [user] or owner ← one rule, using "or"
<note tip>
Reading order: model → schema → type → relations → define.
Outer keywords set the container; inner ones fill in the detail.
</note>
The trick to reading a relation is to say it out loud in plain words. Below, the left side is the DSL, the right side is how you say it.
define owner: [user]
Read it as: “a user can be assigned as the owner.”
Keyword: define. Names: owner, user. This one needs a tuple to be true.
define editor: [user] or owner
Read it as: “an editor is a user assigned directly, or anyone who is already
an owner.”
So every owner is also an editor, for free.
define member: [user, group#member]
Read it as: “a member can be a single user, or every member of a group.”
The comma lists two allowed types. group#member means “the members of a group.”
define viewer: [user:*]
Read it as: “every user can view this.”
The * is a wildcard. Use it for public things.
define can_delete: owner and admin
Read it as: “you can delete only if you are both an owner and an admin.”
and means both conditions must hold at the same time.
define viewer: [user] but not blocked
Read it as: “a viewer is any assigned user, except anyone who is blocked.”
but not removes people, even if they would otherwise qualify.
define viewer: viewer from parent
Read it as: “you can view this if you are a viewer of its parent.”
Follow the parent link, then check viewer on whatever you land on.
define viewer: [user] or editor or viewer from parent
Read it as: “a viewer is a user assigned directly, or an editor, or a
viewer of the parent folder.”
This is the common real-world pattern: direct people, higher roles, and inherited
folder access all at once.
| DSL | Read it as |
|---|---|
[user] | assigned directly, needs a tuple |
[user, team#member] | a user, or every member of a team |
[user:*] | everyone (public) |
or owner | also counts if you are an owner |
and admin | only if you are also an admin |
but not blocked | unless you are blocked |
viewer from parent | if you are a viewer of the parent |
<note tip>
Rule of thumb: [ ] means a direct grant (a tuple sets it). A bare relation
name means a computed grant (worked out from other relations, no tuple needed).
</note>