====== OpenFGA DSL Syntax: Keywords vs Names ======
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.
===== Keywords (fixed — you cannot change them) =====
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. |
===== Names (values — you choose these) =====
Words //you// invent to fit your app.
* **Type names**: ''user'', ''document'', ''folder'', ''organization''. You could call them ''person'' or ''file'' instead.
* **Relation names**: ''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.
===== Symbols (fixed punctuation) =====
* '':'' 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).
===== One line, broken into parts =====
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 |
===== The general shape =====
Uppercase = keywords (fixed). ''<...>'' = names you supply.
MODEL
SCHEMA 1.1
TYPE
RELATIONS
DEFINE : [] OR
===== The keywords as concepts =====
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.
==== model ====
**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
==== schema ====
**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
==== type ====
**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."**
==== relations ====
**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."**
==== define ====
**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."**
==== or / and / but not ====
**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
==== from ====
**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//."**
===== How they nest =====
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"
Reading order: ''model'' → ''schema'' → ''type'' → ''relations'' → ''define''.
Outer keywords set the container; inner ones fill in the detail.
===== More examples to read =====
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.
==== Direct grant ====
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.
==== Union with ''or'' ====
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.
==== More than one allowed type ====
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."
==== Public access with a wildcard ====
define viewer: [user:*]
Read it as: **"every user can view this."** \\
The ''*'' is a wildcard. Use it for public things.
==== Both must be true with ''and'' ====
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.
==== Take away with ''but not'' ====
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.
==== Inherit through a link with ''from'' ====
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.
==== Everything combined ====
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.
===== Quick reading table =====
^ 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 |
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).
===== See also =====
* [[security:authorization:openfga:authorization-model|Authorization model]]
* [[security:authorization:openfga:relationship-tuples|Relationship tuples]]