Add entity design documentation for OpenFGA and API patterns#112
Add entity design documentation for OpenFGA and API patterns#112
Conversation
This documentation provides comprehensive guidance for designing entities, API layouts, and OpenFGA types in the LFX v2 platform. It covers: - Introduction to OpenFGA permissions and relationship-based access control - When to create OpenFGA types vs. entity classifications for indexing - API design patterns and permission mapping - Conditional relationships and optional tuples - Query Service integration and best practices Includes practical GitHub PR example and reference to voting service implementation for developers onboarding to the platform. 🤖 Assisted with GitHub Copilot (via Zed) Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
WalkthroughAdded four new words to the spell-check configuration dictionary and created a comprehensive entity design documentation file covering OpenFGA-based permissions, ReBAC concepts, design guidance, and best practices for the LFX v2 platform. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
dc08854 to
1d9a178
Compare
Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
1d9a178 to
06d186a
Compare
Signed-off-by: Eric Searcy <eric@linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Adds developer-onboarding documentation for LFX v2 entity modeling, tying together OpenFGA types/tuples/relationships with API design and Query Service indexing patterns.
Changes:
- Introduces
docs/entity-design.mdwith guidance and examples for OpenFGA modeling, conditional relationships, and API permission mapping. - Documents entity-type classification patterns for indexing/Query Service and attribute-set splitting for granular permissions.
- Updates
.cspell.jsonto allow new domain terms used in the documentation.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| docs/entity-design.md | New comprehensive design guide for entity modeling, OpenFGA patterns, and API/Query Service conventions. |
| .cspell.json | Adds documentation-related terms to spellchecker allowlist. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| ## Introduction to OpenFGA permissions | ||
|
|
||
| OpenFGA (<https://openfga.dev>) is a relationship-based access control (ReBAC) system, which is a permission system where access decisions take into account a network of relationships between entities. Most systems implementing this derive from the Google Zanzibar paper. For example, in order to have "writer" access to a Google document, I might be a "writer" on the document itself, or I might be a "member" of a team that has "writer" access to folder which is a "parent" of yet another folder that is the "parent" of the document, or I might have "owner" access to a team drive containing that folder! The only check for granting write access is whether I have a "writer" relationship to the document, and this relation will evaluate as true even if it is an transitive relationship across _multiple_ hops in the relationship graph. This kind of access inheritance is also intuitive for users. Nobody has to show them a complex graph of relationships to understand why they have access to a given document: it just works the way they expect. |
There was a problem hiding this comment.
Typo/grammar: "an transitive" should be "a transitive" (or rephrase the sentence) to read naturally.
| OpenFGA (<https://openfga.dev>) is a relationship-based access control (ReBAC) system, which is a permission system where access decisions take into account a network of relationships between entities. Most systems implementing this derive from the Google Zanzibar paper. For example, in order to have "writer" access to a Google document, I might be a "writer" on the document itself, or I might be a "member" of a team that has "writer" access to folder which is a "parent" of yet another folder that is the "parent" of the document, or I might have "owner" access to a team drive containing that folder! The only check for granting write access is whether I have a "writer" relationship to the document, and this relation will evaluate as true even if it is an transitive relationship across _multiple_ hops in the relationship graph. This kind of access inheritance is also intuitive for users. Nobody has to show them a complex graph of relationships to understand why they have access to a given document: it just works the way they expect. | |
| OpenFGA (<https://openfga.dev>) is a relationship-based access control (ReBAC) system, which is a permission system where access decisions take into account a network of relationships between entities. Most systems implementing this derive from the Google Zanzibar paper. For example, in order to have "writer" access to a Google document, I might be a "writer" on the document itself, or I might be a "member" of a team that has "writer" access to folder which is a "parent" of yet another folder that is the "parent" of the document, or I might have "owner" access to a team drive containing that folder! The only check for granting write access is whether I have a "writer" relationship to the document, and this relation will evaluate as true even if it is a transitive relationship across _multiple_ hops in the relationship graph. This kind of access inheritance is also intuitive for users. Nobody has to show them a complex graph of relationships to understand why they have access to a given document: it just works the way they expect. |
| Likewise, in the previous "ACS" LFX RBAC system, we have project roles that inherit policies for how you can interact with a project: managing committees, scheduling meetings, managing mailing lists, etc. However, it cannot "fan out" access to these project resources on a contextual level: | ||
|
|
||
| - LFX with ACS: **project meeting-managers** can access all _past meeting recordings_ for **all meetings associated with the project** | ||
| - LFX with OpenFGA: you can access the _past meeting recording_ if you were **invited to the meeting**, or if you are currently a **member of the committee** that held this meeting in the past, or if you are a **project meeting-manager** on the project the meeting was held on" |
There was a problem hiding this comment.
The second bullet ends with an extra quote character. Remove the trailing '"' so the sentence punctuation is consistent.
| - LFX with OpenFGA: you can access the _past meeting recording_ if you were **invited to the meeting**, or if you are currently a **member of the committee** that held this meeting in the past, or if you are a **project meeting-manager** on the project the meeting was held on" | |
| - LFX with OpenFGA: you can access the _past meeting recording_ if you were **invited to the meeting**, or if you are currently a **member of the committee** that held this meeting in the past, or if you are a **project meeting-manager** on the project the meeting was held on |
|
|
||
| **Note on "maintainer" as a role**: at this time, we do not have a "maintainer" relationship at the project level. Depending on business need, we may choose to track maintainers: | ||
| - As synonymous with the `writer` project relation: we might "brand" project admins as maintainers, understanding that the individuals who are granted this access may not directly correspond to project governance. | ||
| - As a new project relation: if maintainers (as defined by project governance) always get certain access, but it does NOT align to one of our existing roles—or we wish to us it as an abstraction for existing roles ("maintainers get both viewer and meeting manager")—we could add a new maintainer *project relation*. |
There was a problem hiding this comment.
Typo: "wish to us it" should be "wish to use it".
| - As a new project relation: if maintainers (as defined by project governance) always get certain access, but it does NOT align to one of our existing roles—or we wish to us it as an abstraction for existing roles ("maintainers get both viewer and meeting manager")—we could add a new maintainer *project relation*. | |
| - As a new project relation: if maintainers (as defined by project governance) always get certain access, but it does NOT align to one of our existing roles—or we wish to use it as an abstraction for existing roles ("maintainers get both viewer and meeting manager")—we could add a new maintainer *project relation*. |
| If these related/attached resources have their own OpenFGA _type_, meaning they can be uniquely-referenced within OpenFGA tuples, then neither of these applies—see also the section "When to create OpenFGA types". The resource should be served from the "root" of the LFX API. However, value sets or collections may still map to _OpenFGA relations_ on their parent type (e.g. project writers, which is an attribute of project settings, and committee members, which is represented as a sub-collection, are both _relations_ of their respective types). | ||
|
|
||
| **In-Resource Arrays**: | ||
| - **API Design**: Data sets stored as arrays within the main resource: `GET /widget/{id}` returns `{"colors": ["red", "blue"], ...}`. Changes to the collection are made with a PUT to `/widget/{id} containing all properties, including any new/changed set of colors to save. |
There was a problem hiding this comment.
Markdown formatting: the inline code span for the PUT example is missing a closing backtick after /widget/{id} which will break rendering for the rest of the paragraph.
| - **API Design**: Data sets stored as arrays within the main resource: `GET /widget/{id}` returns `{"colors": ["red", "blue"], ...}`. Changes to the collection are made with a PUT to `/widget/{id} containing all properties, including any new/changed set of colors to save. | |
| - **API Design**: Data sets stored as arrays within the main resource: `GET /widget/{id}` returns `{"colors": ["red", "blue"], ...}`. Changes to the collection are made with a PUT to `/widget/{id}` containing all properties, including any new/changed set of colors to save. |
| - **`PUT /projects/{id}/legal`**: requires `owner` to update formation details | ||
| - **`PUT /projects/{id}/settings`**: requires `writer` relation to update non-formation sensitive data | ||
|
|
||
| In this example, there are *3 attributes sets*: |
There was a problem hiding this comment.
Typo: "3 attributes sets" should be "3 attribute sets".
| In this example, there are *3 attributes sets*: | |
| In this example, there are *3 attribute sets*: |
| - **Non-sensitive, non-formation-related metadata**: readably by viewer (everyone, for active projects), writable by writer (any admin) | ||
| - **Sensitive, non-formation-related setting**: readably by auditor, writable by writer |
There was a problem hiding this comment.
Typos: "readably" should be "readable" in these bullets describing attribute sets.
| - **Non-sensitive, non-formation-related metadata**: readably by viewer (everyone, for active projects), writable by writer (any admin) | |
| - **Sensitive, non-formation-related setting**: readably by auditor, writable by writer | |
| - **Non-sensitive, non-formation-related metadata**: readable by viewer (everyone, for active projects), writable by writer (any admin) | |
| - **Sensitive, non-formation-related setting**: readable by auditor, writable by writer |
|
|
||
| **Tuple**: A relation between two _specific_ objects of any type, conforming to the model, which is stored as a fact in OpenFGA. For example: "user:alice is a member of team:frontend" or "project:platform is the parent of project:web-app". The v2 platform maintains a live sync that indexes LFX data (projects, committees, etc.) into corresponding OpenFGA tuples, in real time. | ||
|
|
||
| **Relationship**: A relationship is the traversal or chaining of tuples, as defined by the model, to determine if a given *transitive* relationship exists. For example, if Alice is a member of the frontend team, and the frontend team owns the web-app project, then Alice has an ownership relationship to the web-app project. Throughout this document, when we refer to a "relationship", we always an queried relationship derived from chained tuples and the model definitions, and we will use the term "tuple" when referring to a *direct* relation "fact". The v2 platform uses OpenFGA's "batch check" endpoint to query relationships. |
There was a problem hiding this comment.
There’s a grammar issue in the definition of "Relationship" ("we always an queried relationship"). Rephrase so it clearly states that "relationship" refers to a queried/transitive relationship derived from tuples and the model.
| **Relationship**: A relationship is the traversal or chaining of tuples, as defined by the model, to determine if a given *transitive* relationship exists. For example, if Alice is a member of the frontend team, and the frontend team owns the web-app project, then Alice has an ownership relationship to the web-app project. Throughout this document, when we refer to a "relationship", we always an queried relationship derived from chained tuples and the model definitions, and we will use the term "tuple" when referring to a *direct* relation "fact". The v2 platform uses OpenFGA's "batch check" endpoint to query relationships. | |
| **Relationship**: A relationship is the traversal or chaining of tuples, as defined by the model, to determine if a given *transitive* relationship exists. For example, if Alice is a member of the frontend team, and the frontend team owns the web-app project, then Alice has an ownership relationship to the web-app project. Throughout this document, when we refer to a "relationship", we always mean a queried (possibly transitive) relationship derived from chained tuples and the model definitions, and we will use the term "tuple" when referring to a *direct* relation "fact". The v2 platform uses OpenFGA's "batch check" endpoint to query relationships. |
|
|
||
| ### When to create separate collection endpoints | ||
|
|
||
| When a entity or resource has an attribute which is itself a set of values or a collection of objects, it may be able to be represented either as an array within that resource, or, as its own endpoint implementing REST semantics (POST/PUT/DELETE) to add & remove entries. |
There was a problem hiding this comment.
Grammar: "When a entity" should be "When an entity".
| When a entity or resource has an attribute which is itself a set of values or a collection of objects, it may be able to be represented either as an array within that resource, or, as its own endpoint implementing REST semantics (POST/PUT/DELETE) to add & remove entries. | |
| When an entity or resource has an attribute which is itself a set of values or a collection of objects, it may be able to be represented either as an array within that resource, or, as its own endpoint implementing REST semantics (POST/PUT/DELETE) to add & remove entries. |
| | 4. Sub-collections | `committee_member:{mbr_id}` | Query Service | `/committees/{id}/` `members/{mbr_id}` | Indexed as `committee_member` - permission (API path & Query Service) is a relation to the `committee` OpenFGA type. | | ||
| | 5. Attribute sets of sub-collections | `committee_member_priv:{mbr_id}` | Query Service | `/committees/{id}/` `members/{mbr_id}/priv` | Indexed as `committee_member_priv` - permission (API path & Query Service) is a relation to the `committee` OpenFGA type. | | ||
| | 6. Sub-collections (distinct type) | `domain:{dom_id}` | Query Service | `/projects/{id}/` `domains/{dom_id}` | Functionally identical to a "sub-collection", including needing the OpenFGA type as an API path prefix, but dropping the prefix from our indexer type for brevity. | | ||
| | 7. Attribute sets of #6 | `domain_restricted:{dom_id}` | Query Service | `/projects/{id}/` `domains/{dom_id}/restricted` | Functionally identical to #5, but with the leading type dropped as with #6. | |
There was a problem hiding this comment.
In the Attribute naming table, the API path column splits a single path into multiple inline-code spans (e.g., /committees/{id}/ members/{mbr_id}), which renders with a space and looks like two separate tokens. Use a single code span per path (e.g., /committees/{id}/members/{mbr_id}) for clarity/copy-paste correctness.
| | 4. Sub-collections | `committee_member:{mbr_id}` | Query Service | `/committees/{id}/` `members/{mbr_id}` | Indexed as `committee_member` - permission (API path & Query Service) is a relation to the `committee` OpenFGA type. | | |
| | 5. Attribute sets of sub-collections | `committee_member_priv:{mbr_id}` | Query Service | `/committees/{id}/` `members/{mbr_id}/priv` | Indexed as `committee_member_priv` - permission (API path & Query Service) is a relation to the `committee` OpenFGA type. | | |
| | 6. Sub-collections (distinct type) | `domain:{dom_id}` | Query Service | `/projects/{id}/` `domains/{dom_id}` | Functionally identical to a "sub-collection", including needing the OpenFGA type as an API path prefix, but dropping the prefix from our indexer type for brevity. | | |
| | 7. Attribute sets of #6 | `domain_restricted:{dom_id}` | Query Service | `/projects/{id}/` `domains/{dom_id}/restricted` | Functionally identical to #5, but with the leading type dropped as with #6. | | |
| | 4. Sub-collections | `committee_member:{mbr_id}` | Query Service | `/committees/{id}/members/{mbr_id}` | Indexed as `committee_member` - permission (API path & Query Service) is a relation to the `committee` OpenFGA type. | | |
| | 5. Attribute sets of sub-collections | `committee_member_priv:{mbr_id}` | Query Service | `/committees/{id}/members/{mbr_id}/priv` | Indexed as `committee_member_priv` - permission (API path & Query Service) is a relation to the `committee` OpenFGA type. | | |
| | 6. Sub-collections (distinct type) | `domain:{dom_id}` | Query Service | `/projects/{id}/domains/{dom_id}` | Functionally identical to a "sub-collection", including needing the OpenFGA type as an API path prefix, but dropping the prefix from our indexer type for brevity. | | |
| | 7. Attribute sets of #6 | `domain_restricted:{dom_id}` | Query Service | `/projects/{id}/domains/{dom_id}/restricted` | Functionally identical to #5, but with the leading type dropped as with #6. | |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/entity-design.md`:
- Line 7: Fix multiple typos in the entity-design document by replacing specific
incorrect phrases with the provided corrections: change "it is an transitive
relationship" to "it is a transitive relationship" (remove the extra "an");
change "we always an queried relationship" to "we always mean a queried
relationship"; remove the extra parenthesis in "closer))" → "closer)"; change
"etc)" to "etc.)" (add period before close-paren); change "When a entity" to
"When an entity"; change "3 attributes sets" to "3 attribute sets"; change
"readably by viewer" to "readable by viewer"; and change "readably by auditor"
to "readable by auditor". Locate these exact strings in the document and apply
the replacements ensuring punctuation and article fixes are applied as
specified.
- Line 30: The document uses asterisk-based italics (e.g., *transitive*,
*direct*) which violates MD049; replace all instances of asterisk emphasis with
underscore emphasis (e.g., _transitive_, _direct_) on the flagged occurrences
including the "**Relationship**" paragraph and other lines referencing
"relationship" and "tuple" to conform to the configured style; ensure you change
only single-asterisk italics to single-underscore italics and leave bold
(double-asterisk) or code backticks untouched.
- Around line 50-73: The OpenFGA DSL uses '#' to reference a type's relation, so
replace all userset references using “[team:member]” with “[team#member]” and
similarly “[organization:member with is_internal_visibility]” with
“[organization#member with is_internal_visibility]”; update occurrences inside
the repository and pullrequest relation definitions (symbols: type repository,
relations define writer, define reader, type pullrequest, relations define
writer/reader/closer) and any explanatory text that mentions “[team:member]” or
“[organization:member …]” so the model uses the correct userset syntax.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
docs/google-doc-rebac.excalidraw.pngis excluded by!**/*.png
📒 Files selected for processing (2)
.cspell.jsondocs/entity-design.md
|
|
||
| ## Introduction to OpenFGA permissions | ||
|
|
||
| OpenFGA (<https://openfga.dev>) is a relationship-based access control (ReBAC) system, which is a permission system where access decisions take into account a network of relationships between entities. Most systems implementing this derive from the Google Zanzibar paper. For example, in order to have "writer" access to a Google document, I might be a "writer" on the document itself, or I might be a "member" of a team that has "writer" access to folder which is a "parent" of yet another folder that is the "parent" of the document, or I might have "owner" access to a team drive containing that folder! The only check for granting write access is whether I have a "writer" relationship to the document, and this relation will evaluate as true even if it is an transitive relationship across _multiple_ hops in the relationship graph. This kind of access inheritance is also intuitive for users. Nobody has to show them a complex graph of relationships to understand why they have access to a given document: it just works the way they expect. |
There was a problem hiding this comment.
Several grammar/typo errors across the document.
Consolidated list of issues to fix:
| Line | Current | Fix |
|---|---|---|
| 7 | it is an transitive relationship |
it is a transitive relationship |
| 30 | we always an queried relationship |
we always mean a queried relationship |
| 120 | closer)) |
closer) (extra )) |
| 237 | etc) |
etc.) (period before close-paren per American English) |
| 248 | When a entity |
When an entity |
| 283 | 3 attributes sets |
3 attribute sets |
| 286 | readably by viewer |
readable by viewer |
| 287 | readably by auditor |
readable by auditor |
🧰 Tools
🪛 LanguageTool
[style] ~7-~7: ‘take into account’ might be wordy. Consider a shorter alternative.
Context: ...ermission system where access decisions take into account a network of relationships between enti...
(EN_WORDINESS_PREMIUM_TAKE_INTO_ACCOUNT)
[style] ~7-~7: Consider a more concise word here.
Context: ...the Google Zanzibar paper. For example, in order to have "writer" access to a Google docume...
(IN_ORDER_TO_PREMIUM)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/entity-design.md` at line 7, Fix multiple typos in the entity-design
document by replacing specific incorrect phrases with the provided corrections:
change "it is an transitive relationship" to "it is a transitive relationship"
(remove the extra "an"); change "we always an queried relationship" to "we
always mean a queried relationship"; remove the extra parenthesis in "closer))"
→ "closer)"; change "etc)" to "etc.)" (add period before close-paren); change
"When a entity" to "When an entity"; change "3 attributes sets" to "3 attribute
sets"; change "readably by viewer" to "readable by viewer"; and change "readably
by auditor" to "readable by auditor". Locate these exact strings in the document
and apply the replacements ensuring punctuation and article fixes are applied as
specified.
|
|
||
| **Tuple**: A relation between two _specific_ objects of any type, conforming to the model, which is stored as a fact in OpenFGA. For example: "user:alice is a member of team:frontend" or "project:platform is the parent of project:web-app". The v2 platform maintains a live sync that indexes LFX data (projects, committees, etc.) into corresponding OpenFGA tuples, in real time. | ||
|
|
||
| **Relationship**: A relationship is the traversal or chaining of tuples, as defined by the model, to determine if a given *transitive* relationship exists. For example, if Alice is a member of the frontend team, and the frontend team owns the web-app project, then Alice has an ownership relationship to the web-app project. Throughout this document, when we refer to a "relationship", we always an queried relationship derived from chained tuples and the model definitions, and we will use the term "tuple" when referring to a *direct* relation "fact". The v2 platform uses OpenFGA's "batch check" endpoint to query relationships. |
There was a problem hiding this comment.
MD049: Replace asterisk emphasis with underscore emphasis throughout.
markdownlint-cli2 reports MD049 violations (emphasis-style: expected underscore, actual asterisk) on lines 30, 32, 206, 224, 241, 274, 283, and 289. All italic emphasis should use _text_ instead of *text* to be consistent with the configured style.
Example fix (representative; the same pattern applies to all flagged lines):
-we will use the term "tuple" when referring to a *direct* relation
+we will use the term "tuple" when referring to a _direct_ relation📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| **Relationship**: A relationship is the traversal or chaining of tuples, as defined by the model, to determine if a given *transitive* relationship exists. For example, if Alice is a member of the frontend team, and the frontend team owns the web-app project, then Alice has an ownership relationship to the web-app project. Throughout this document, when we refer to a "relationship", we always an queried relationship derived from chained tuples and the model definitions, and we will use the term "tuple" when referring to a *direct* relation "fact". The v2 platform uses OpenFGA's "batch check" endpoint to query relationships. | |
| **Relationship**: A relationship is the traversal or chaining of tuples, as defined by the model, to determine if a given *transitive* relationship exists. For example, if Alice is a member of the frontend team, and the frontend team owns the web-app project, then Alice has an ownership relationship to the web-app project. Throughout this document, when we refer to a "relationship", we always an queried relationship derived from chained tuples and the model definitions, and we will use the term "tuple" when referring to a _direct_ relation "fact". The v2 platform uses OpenFGA's "batch check" endpoint to query relationships. |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 30-30: Emphasis style
Expected: underscore; Actual: asterisk
(MD049, emphasis-style)
[warning] 30-30: Emphasis style
Expected: underscore; Actual: asterisk
(MD049, emphasis-style)
[warning] 30-30: Emphasis style
Expected: underscore; Actual: asterisk
(MD049, emphasis-style)
[warning] 30-30: Emphasis style
Expected: underscore; Actual: asterisk
(MD049, emphasis-style)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/entity-design.md` at line 30, The document uses asterisk-based italics
(e.g., *transitive*, *direct*) which violates MD049; replace all instances of
asterisk emphasis with underscore emphasis (e.g., _transitive_, _direct_) on the
flagged occurrences including the "**Relationship**" paragraph and other lines
referencing "relationship" and "tuple" to conform to the configured style;
ensure you change only single-asterisk italics to single-underscore italics and
leave bold (double-asterisk) or code backticks untouched.
| define member: [user] or [team:member] | ||
|
|
||
| type repository | ||
| relations | ||
| define organization: [organization] | ||
| define writer: [user] or [team:member] or owner from organization | ||
| # Unlike writer, we don't inherit readers from organization members, because | ||
| # this depends on a "condition": the visibility configuration of the repo. | ||
| # See the "conditional relations" section of this doc for more info. | ||
| define reader: [user:*] or [user] or [team:member] or writer | ||
|
|
||
| type pullrequest | ||
| relations | ||
| define repository: [repository] | ||
| define author: [user] | ||
| define writer: writer from repository | ||
| define closer: author or writer | ||
| define reader: author or writer or reader from repository | ||
| ``` | ||
|
|
||
| Considerations: | ||
| - There is no "singleton" relation definition, for example, that a pull request only may have (or must have) exactly **one** repository relation. These kinds of data constraints are enforced by the system that creates and deletes tuples into OpenFGA, not by OpenFGA itself. | ||
| - Ordinarily, "greater" relations will explicitly cascade into "lesser" relations (owner → writer → reader), because any given permission (the allow/deny rule defined for a given URL and method) should only check a _single_ relationship. Rather than "writers or readers can GET this resource", you want "readers can GET this resource", and then you include writers as having the readers relation automatically. This also is why a relation like "closer" is defined in the model: it's just an abstraction over two other relations, so that the permission can be defined with only a single relationship to check. | ||
| - The `[team:member]` syntax means that the tuple will point to a team, but when evaluated, it's actually a reference to the "members" relation of that team. The example model not only allows configuring teams as repo readers or writers, but it also allows teams to include other teams (nested teams). |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
OpenFGA DSL userset reference syntax # vs : documentation
💡 Result:
In OpenFGA, : and # mean different things:
type:id(uses:) is the canonical syntax for an object (and also for a user identifier), wheretypeis the object type andidis its identifier (e.g.document:budget,user:anne). [2]object#relation(uses#) is the canonical syntax for a userset: “all users that haverelationonobject” (e.g.org:xyz#member,company:xyz#employee). [1], [2]
In the DSL, you’ll see usersets appear anywhere a userset is allowed, e.g. in type restrictions like [user, domain#member] (meaning the relation can be directly assigned either a user or the userset domain:<id>#member). [3]
Sources
[1] OpenFGA Docs – Usersets https://openfga.dev/docs/modeling/building-blocks/usersets
[2] OpenFGA Docs – Concepts https://openfga.dev/docs/concepts
[3] OpenFGA Docs – Configuration Language https://openfga.dev/docs/configuration-language
🏁 Script executed:
# Check if the file exists and examine the lines mentioned
find . -name "entity-design.md" -type fRepository: linuxfoundation/lfx-v2-helm
Length of output: 94
🏁 Script executed:
# Get the file size and check content around the mentioned lines
wc -l ./docs/entity-design.md
echo "---"
# Check lines 48-75 (around the first issue)
sed -n '48,75p' ./docs/entity-design.md | cat -nRepository: linuxfoundation/lfx-v2-helm
Length of output: 2269
🏁 Script executed:
# Check lines 181, 198, and 218 for additional instances
echo "=== Line 181 context (175-185) ==="
sed -n '175,185p' ./docs/entity-design.md | cat -n
echo ""
echo "=== Line 198 context (192-205) ==="
sed -n '192,205p' ./docs/entity-design.md | cat -n
echo ""
echo "=== Line 218 context (210-225) ==="
sed -n '210,225p' ./docs/entity-design.md | cat -nRepository: linuxfoundation/lfx-v2-helm
Length of output: 3240
Fix OpenFGA DSL syntax: change [team:member] to [team#member] throughout the file.
In OpenFGA DSL, # separates a type from a relation to form a userset reference (e.g., team#member), while : separates a type from an object ID (e.g., user:alice). The current model incorrectly uses : instead of # for userset type restrictions.
Affected locations:
- Line 50:
[team:member]→[team#member] - Line 55:
[team:member]→[team#member] - Line 59:
[team:member]→[team#member] - Line 73: Explanatory text references
[team:member]→ update to[team#member] - Line 181:
[team:member]→[team#member] - Line 198:
[organization:member with is_internal_visibility]→[organization#member with is_internal_visibility] - Line 218:
[team:member]→[team#member]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/entity-design.md` around lines 50 - 73, The OpenFGA DSL uses '#' to
reference a type's relation, so replace all userset references using
“[team:member]” with “[team#member]” and similarly “[organization:member with
is_internal_visibility]” with “[organization#member with
is_internal_visibility]”; update occurrences inside the repository and
pullrequest relation definitions (symbols: type repository, relations define
writer, define reader, type pullrequest, relations define writer/reader/closer)
and any explanatory text that mentions “[team:member]” or “[organization:member
…]” so the model uses the correct userset syntax.
This documentation provides comprehensive guidance for designing entities, API layouts, and OpenFGA types in the LFX v2 platform. It covers:
Includes practical GitHub PR example and reference to voting service implementation for developers onboarding to the platform.
🤖 Assisted with GitHub Copilot (via Zed)