new blog on upcoming changes for weighted graph models#1259
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughA new blog post announces OpenFGA's rollout of weighted graph-based Check resolution. It contrasts the legacy recursive approach with build-time graph construction, details breaking changes in model validation and Check behavior, and provides migration guidance including error scenarios, validation checklists, timeline, and community resources. ChangesWeighted Graph Migration Announcement
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@blog/weighted-graph-upcoming-changes.md`:
- Line 14: Change the wording for clarity in the file's intro and model-error
sentence: replace the phrase “update algorithm” with “updated algorithm” in the
sentence that begins “OpenFGA is continuing to roll out a **weighted graph-based
resolution algorithm**…” and change the verb usage “models fallback” (around
line referencing model errors) to “models fall back” or “models revert to the
legacy algorithm” so the phrasing reads naturally and consistently.
- Around line 143-146: The fenced code blocks containing example calls like
check("document:report", "viewer", "document:contract#owner"),
check("document:readme", "viewer", "user:*"), check("document:report", "viewer",
"user:alice"), the blocks with write(document:source#allowed, viewer,
document:target), and the other examples are missing language identifiers;
update each triple-backtick fence to include a language (e.g., ```text) so lint
rule MD040 is satisfied and syntax highlighting works, ensuring you modify every
block that wraps calls to check(...) and write(...) as noted in the review.
- Line 234: Update the heading text "How to Check If You're Model Is Affected"
to use the possessive form by replacing "You're" with "Your" so it reads "How to
Check If Your Model Is Affected"; locate and edit the heading string in the
markdown (the line containing that exact heading) to correct the typo.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f9957dc6-a926-445e-974f-d4af69cc4a72
📒 Files selected for processing (1)
blog/weighted-graph-upcoming-changes.md
|
There was a problem hiding this comment.
Pull request overview
Adds a new blog post describing OpenFGA’s upcoming weighted graph-based Check resolution changes, the incompatible modeling/check patterns, and migration guidance.
Changes:
- Introduces a new announcement/migration blog post.
- Documents five affected patterns with examples and suggested fixes.
- Adds timeline and support links for users preparing migrations.
Comments suppressed due to low confidence (9)
blog/weighted-graph-upcoming-changes.md:69
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:80
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:97
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:106
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:117
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:135
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:150
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:184
- OpenFGA model snippets elsewhere use
dsl.openfgafences for site syntax highlighting (for example, blog/conditional-tuples-announcement.md:23 and docs/content/getting-started/perform-list-users.mdx:100). Using plaindslhere deviates from that convention and may lose OpenFGA-specific highlighting.
```dsl
blog/weighted-graph-upcoming-changes.md:256
- This sentence is missing the article before "weighted graph algorithm"; it should read "the weighted graph algorithm".
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| ``` | ||
| write(document:source#allowed, viewer, document:target) # Already stored | ||
| write(document:source#reader, viewer, document:target) # Store explicitly |
There was a problem hiding this comment.
Copilot is right about this. The issue is that you can't actually define a tuple for the reader relation since it's not in the model definition. So we're no longer supporting check resolution calls against aliases for usersets.
| write(document:source#reader, viewer, document:target) # Store explicitly |
| **Fix:** Split into a "base" relation (allows recursion) and an "allowed" relation (applies the access gate): | ||
|
|
||
| ```dsl |
| define viewer: member from parent # but folder has no member, only viewer! | ||
| ``` | ||
|
|
||
| **Why this fails:** Access checks through `folder` as `parent` silently return `false` — users never get access even if they should. |
There was a problem hiding this comment.
This is a little inaccurate. Why it fails is because the weighted graph won't build a graph where all the nodes are not reachable, not because of a false returned during a check.
| **Why this fails:** Access checks through `folder` as `parent` silently return `false` — users never get access even if they should. | |
| **Why this fails:** The weighted graph cannot build a graph with unreachable nodes. Since `member` does not exist on `folder`, when the `document.viewer` part of the graph attempts to reach all the `member` relations in `parent`, it fails and does not build. |
| title: "OpenFGA's Move to Weighted Graph Resolution: What's Changing" | ||
| description: "OpenFGA is transitioning to a weighted graph-based resolution algorithm. Learn what's changing, why, and how to migrate your models." | ||
| slug: weighted-graph-upcoming-changes | ||
| date: 2026-05-14 |
There was a problem hiding this comment.
Need to update this
| date: 2026-05-14 | |
| date: 2026-06-17 |
| ``` | ||
|
|
||
| > **Migration impact:** Your existing `member` and `blocked` tuples stay exactly as they are — no tuple writes or deletes needed. The only change is in how your application calls Check: replace `check(user, "member", object)` with `check(user, "allowed_member", object)`. The new `allowed_member` relation reads from the same underlying data, just with the exclusion gate applied at the right level. | ||
|
|
There was a problem hiding this comment.
This might be too in-depth for this article and better suited to a migration guide, but I just want to capture that some users might want to be able to express hierarchical blocked group membership. How can they do this if we no longer allow the above model? By moving the hierarchy into a TTU relation. Here is an example:
type group
relations
define blocked: [user, group#member]
define blocked_in_hierarchy: blocked or blocked_in_hierarchy from hierarchy_edge
define hierarchy_edge: [group]
define member: [user, group#member]
define allowed_member: member but not blocked_in_hierarchy
However, this would require tuple migration since any pre-existing (group:X#member, member, group:Y) relations would need to be rewritten as (group:X, hierarchy_edge, group:Y), as well as changing their check calls from member to allowed_member. This gets a little complicated to explain, however, and might even require being in its own advanced modelling guide.
| relations | ||
| define owner: [user] | ||
| define member: [user] | ||
| define viewer: [document#owner] but not member |
There was a problem hiding this comment.
There's a mistake here, you need to add user in order for the example to work. Eg,
| define viewer: [document#owner] but not member | |
| define viewer: [user, document#owner] but not member |
| check("user:alice", "viewer", "document:report") # ✓ — userset exclusion applied correctly per user | ||
| check("user:alice", "viewer", "document:readme") # ✓ — wildcard exclusion applied correctly per user | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Note: This might be better suited to a migration guide, but we could also suggest that users leverage ListUsers here. Eg,
If you need to enumerate who actually has access, use ListUsers:
listUsers("document:report", "viewer") → [user:alice, user:bob]
ListUsers handles both userset and wildcard sources correctly, returning the concrete set of allowed users (with exclusions applied). Note that for wildcard-based relations, this set may be very large, therefore if your original intent was a yes/no "is this fully public?" question, consider modelling publicness as a separate exclusion-free relation rather than reasoning about the full user set.
|
|
||
| > **Migration impact:** | ||
| > 1. First, check whether your model has any relations that accept a group reference as a value — look for type lists that include `type:object#relation` (e.g., `define viewer: [user, document#allowed]`). If none of your relations accept group references, this change does not affect you. | ||
| > 2. If they do exist in your model, audit your check calls for any that pass a group reference as the user. Verify that the relation name in the check matches the relation name used when the tuple was written. If your application relied on alias inference — checking with `#reader` when `#allowed` was stored — update those calls to use the stored relation name, or write additional tuples to explicitly cover the names you check with. |
There was a problem hiding this comment.
| > 2. If they do exist in your model, audit your check calls for any that pass a group reference as the user. Verify that the relation name in the check matches the relation name used when the tuple was written. If your application relied on alias inference — checking with `#reader` when `#allowed` was stored — update those calls to use the stored relation name, or write additional tuples to explicitly cover the names you check with. | |
| > 2. If they do exist in your model, audit your check calls for any that pass a group reference as the user. Verify that the relation name in the check matches the relation name used in the model relation. If your application relied on alias inference — checking with `#reader` when `#allowed` was stored — update those calls to use the stored relation name. |
|
|
||
| #### 5. Self-Referential Usersets | ||
|
|
||
| **What was broken:** Previously, asking "do the viewers of document A have access to document A?" like `check("document:A#viewer", "viewer", "document:A")`, the legacy algorithm always evaluated `TRUE`, just because the relation existed in the model, not because an actual tuple granted that access. |
There was a problem hiding this comment.
| **What was broken:** Previously, asking "do the viewers of document A have access to document A?" like `check("document:A#viewer", "viewer", "document:A")`, the legacy algorithm always evaluated `TRUE`, just because the relation existed in the model, not because an actual tuple granted that access. | |
| **What was broken:** Previously, asking "do the viewers of document A have access to document A?" like `check("document:A#viewer", "viewer", "document:A")`, the legacy algorithm always evaluated `TRUE`, just because the relation existed in the model and the queried user id and object id matched, not because an actual tuple granted that access. |
|
|
||
| **Next**: | ||
| - Check transitions to the weighted graph algorithm as default, also with a fallback. | ||
| - Provide a CLI command `fga model validate` to test if your model contains any issues and needs a migration. |
There was a problem hiding this comment.
We don't have this yet, is the intention to communicate that we will have it? Should be more clear:
| - Provide a CLI command `fga model validate` to test if your model contains any issues and needs a migration. | |
| - A CLI command `fga model validate` to test if your model contains any issues and needs a migration will be coming soon. |
| - ListObjects runs on the weighted graph algorithm, with a fallback to the legacy algorithm for incompatible models. | ||
|
|
||
| **Next**: | ||
| - Check transitions to the weighted graph algorithm as default, also with a fallback. |
There was a problem hiding this comment.
| - Check transitions to the weighted graph algorithm as default, also with a fallback. | |
| - Check runs on the weighted graph algorithm as default, also with a fallback. |
| **Next**: | ||
| - Check transitions to the weighted graph algorithm as default, also with a fallback. | ||
| - Provide a CLI command `fga model validate` to test if your model contains any issues and needs a migration. | ||
| - Date announced for final deadline to migrate models before new versions of OpenFGA will only support weighted graph algorithm. |
There was a problem hiding this comment.
| - Date announced for final deadline to migrate models before new versions of OpenFGA will only support weighted graph algorithm. | |
| - We will be announcing a final deadline for migrating incompatible models, after which new OpenFGA versions will only support the weighted graph algorithm. |
Summary
Summary by CodeRabbit
Release Notes