A relationship tuple is one small fact about who is connected to what. It is the data in OpenFGA. The authorization model defines the shape of the rules; tuples fill in the real facts.
The model alone answers nothing. It only says a document can have an owner. A tuple says who the owner actually is. You need thousands or millions of these facts to describe your real users, groups, and objects.
Every tuple has three parts, plus an optional condition.
| Part | Meaning | Example |
|---|---|---|
| user | who or what the fact is about | user:anne |
| relation | the named link (must exist in the model) | owner |
| object | the thing being accessed (type:id) | document:budget |
Written together:
user:anne owner document:budget
Read it as: “Anne is the owner of the budget document.”
The user field is flexible. It can be:
user:anne — one specific subject.group:eng#member — every member of the eng group. user:* — every user of that type (public access).
The name of the link, like owner, editor, or viewer. It must be a
relation that exists in the model for that object's type. You cannot invent one.
The target, always in type:id form, like document:budget or
organization:acme. The type must exist in the model.
user:anne owner document:budget user:bob viewer document:budget group:eng#member viewer document:roadmap folder:finance parent document:budget user:* viewer document:public-notice
Notice the fourth line: the user is itself an object (folder:finance). This
links a document to its parent folder, so access can flow along that link.
A tuple can carry a condition and some context. The grant only counts if the condition is true. For example, a tuple might say “Anne is a viewer only if the current time is during work hours”. This mixes attribute checks into the data.
You change tuples through the Write API.
Tuples are usually kept in sync with your main database. A common pattern is CDC (Change Data Capture): when a row changes in your database, a matching tuple is written or deleted in OpenFGA automatically.
Some facts are not stored. You can pass contextual tuples inside a single Check call. They exist only for that one question and are never saved.
<note tip> Use contextual tuples for facts you know at request time but do not want to store, such as the user's current department passed in from a request header. </note>
Input → Process → Output:
allowed: true or allowed: false answer.