The pattern
- The user asks the agent a question.
- The retriever pulls candidate documents (vector search, keyword, hybrid).
- OpenFGA filters the candidates: for each candidate
doc:X, check whetheruser:Yhascan_view. Or call list-objects once to get the full set of documents this user can read, then intersect. - Only the surviving documents are passed to the model as context.
Simplified model
Folders nest arbitrarily deep — a viewer on a top-level folder inheritscan_view on every descendant folder and document. Before retrieval reaches the model, call list-objects(user:alice, can_view, document) to get every document Alice can read, then intersect with the retriever’s candidate set.
Why list-objects matters here
For small set of documents, per-document checks are fine. For larger ones,list-objects is dramatically cheaper: one call returns the full set of documents the user can read, and you intersect that with the retriever’s candidates. This is exactly the case OpenFGA’s reverse queries are designed for.