Guides › Secure RAG › Permissions
RAG Permissions: Enforcing Entitlements at Query Time
A RAG pipeline enforces permissions correctly when it evaluates the requesting user's entitlements at the moment of the query, against metadata carried by every chunk, as a filter applied before similarity search. Most deployments do none of those three things, and each omission leaks differently.
Why do index-time permissions fail?
Because the index is built by a connector authenticating as a service account with broad read scope. That is the only practical way to build a complete index on a schedule, so it is what nearly everyone does. The index ends up holding the union of what every user can see.
Nothing about that is wrong on its own. It becomes a leak the moment retrieval serves from that union without re-checking who is asking. The permission model was captured once, at build time, for an identity that is not the one making the request.
Why is post-filtering not good enough?
Post-filtering retrieves the top-k most similar chunks, then discards the ones the user cannot access. It is the default in most frameworks because it is trivial to bolt on. It fails in three distinct ways.
Ranking degrades. If eight of your top ten results are dropped, the user receives the ninth and tenth best answers and has no idea. Quality complaints follow, and the usual fix is to raise k, which widens the exposure.
Rerankers still see the text. Many stacks rerank before filtering, which means a cross-encoder processed content the user was not entitled to. Whether that constitutes disclosure depends on where the reranker runs and what it logs.
And filtering is observable. Result counts, latency and pagination behaviour can all confirm that a matching document exists even when its content is withheld. Existence is often the sensitive fact, particularly for legal holds, unannounced transactions and personnel matters.
What does a pre-filter look like?
The retriever resolves the requesting identity to a set of permission attributes, then constrains the candidate set to chunks carrying matching attributes before vector similarity is computed. Unentitled chunks are never scored, never reranked, and never counted.
Most production vector stores support metadata filtering that executes as part of the search rather than after it. The engineering work is rarely in the database. It is in making sure every chunk carries current, correct permission attributes, which is where these projects actually stall.
How should permission metadata reach the chunk?
Inherited through chunking, refreshed on change, and expressed as attributes the retriever can evaluate cheaply. When a 90-page document becomes 400 chunks, each chunk needs the access decision that governed the original. Pipelines that store only a source URI leave the retriever nothing to filter on.
Group identifiers usually work better than user lists, because they survive personnel changes without a re-index. The tradeoff is that group membership then has to be resolved at query time against the identity provider, which adds a lookup to the hot path and is worth caching carefully.
Watch for documents that never carried meaningful permissions. Scanned files in a shared folder, exports in object storage, channels that were public by default. Label inheritance gives you nothing there, and those items are frequently the most sensitive in the corpus.
How fast do permissions need to refresh?
As fast as they change in the source system. Nightly re-indexing leaves up to 24 hours during which a revoked user still matches chunks they can no longer open. Offboarding is the case that matters most, because the source system revokes access instantly while the index keeps serving.
How do you verify it works?
Test it rather than reasoning about it. Build a ground-truth entitlement matrix from source-system access control lists, run the same queries as identities at different access levels, and compare what came back against what each was entitled to. The procedure is written up as a RAG leakage self-test.
Re-run after every re-index. Chunk-level permission metadata is the thing that most often fails to survive a rebuild, and a rebuild rarely announces itself as a security event.
Frequently asked questions
Can we use the same ACLs as the source system?
You should, as the source of truth, but they usually need translating. Source systems express permissions as inheritance chains and group hierarchies; a retriever needs flat attributes it can filter on in a single pass. The translation is where errors enter, so test the result rather than the intention.
What about documents with no owner or no permissions?
Default them closed and surface them for review. Treating unlabeled content as public is the most common way genuinely sensitive material ends up in an index, because the files nobody governs are frequently the files nobody should have left lying around.
Does per-user filtering hurt retrieval quality?
A pre-filter does not, because scoring happens across the entitled candidate set and returns the best entitled results. Post-filtering does hurt quality, which is one of the reasons to move the filter earlier rather than compensate by widening k.
Is this the same as row-level security?
It is the same idea applied to a vector index, with one extra failure mode. Row-level security enforces the attributes you wrote; it has no view of whether those attributes still reflect the source system. Freshness is your problem, not the database's.
