Solution Architecture14 October 20268 min read

RAG That Survives Production — Part 5 of 8

Permission-aware retrieval: the same rules as the source system

An employee should never see information they are not entitled to because an embedding happened to sit nearby. Part 5 covers filtering before ranking, why post-filtering fails twice, derived documents that lose their permissions, the lag between a source change and the index, and how to prove isolation with tests.

#RAG#AI Governance#Zero Trust Security#Data Governance#Solution Architecture

Every document in a well-run organisation already has rules about who may read it. The rate schedules sit in a restricted folder. The joint-venture bid sits in a site shared only with the bid team. The disciplinary file sits somewhere very few people can reach.

A RAG system copies all of that content into a new place, an index, and then answers questions from it. Unless the rules come along with the content, the index becomes the one place in the organisation where anything can be found by anyone who asks the right question. The assistant does not need to be malicious for this to happen. It only needs a relevant passage to sit close to a question in meaning space.

The principle is short: a RAG system must enforce the same permissions as the source system, on every query, at the moment of the query. Living up to it is not. This part goes beyond the basics covered in RAG is a data security system, which explains why identity must enter before retrieval and what Azure AI Search offers. Here the focus is on the design mistakes that survive even when a team knows the principle.

Bergvliet Engineering is fictional, and its systems and figures are illustrative. Its commercial rate schedules are restricted to a twelve-person commercial team. Some bids are prepared in joint ventures with other firms, and those documents are shared only with the named joint-venture bid team under the joint-venture agreement.

Filter first, not afterwards

There are two ways to keep restricted chunks out of an answer.

Pre-filtering makes permission part of the search itself. Each chunk carries the access rule of its source document, and the query includes the caller's identities and groups, so retrieval only ever ranks chunks the caller may see.

Post-filtering searches everything, takes the top results, and then removes the ones the caller may not see.

Both can stop restricted text reaching the answer. Only pre-filtering reliably returns a useful one.

Filter first, or filter afterwardsPRE-FILTER: PERMISSION IS PART OF THE QUERYQuestion +caller's groupsSearch only chunksthe caller may seeTop 8: all usablePOST-FILTER: PERMISSION IS CHECKED AT THE ENDQuestionSearcheverythingTop 8Drop forbidden:2 left, or nonePost-filtering can also leak through result counts, snippets, suggestions, timings and caches.
Both designs can stop restricted text reaching the answer. Only one of them returns a useful result. Filtering after ranking leaves a site engineer with two usable chunks, or none, out of eight, and every step before the filter has already touched content the engineer may not see.

Bergvliet's first permission design was a post-filter, because it was easy to add to the existing pipeline. It failed in two ways.

The first was quality. A site engineer asking about night-work rates on road jobs would match the commercial rate schedules strongly, because they were the most relevant content. The top eight results were mostly schedules. The post-filter removed them, leaving two loosely related method statements, or on some questions nothing at all. The assistant answered from whatever was left, badly. The engineer was not given restricted information, but was given a worse answer than a permission-aware search would have produced from the content they could see.

The second was leakage around the edges. Even when no restricted text reached the answer, the restricted content had still shaped the request: result counts ("8 results found, 2 shown"), snippets in a debug view, autocomplete suggestions built from all indexed titles, and response times that varied with how much was filtered. A semantic answer cache keyed on the question, not on the caller, could hand a commercial manager's earlier answer to a site engineer outright. Any of these can reveal that something exists, and sometimes what it says.

Permissions have to be captured, not assumed

Pre-filtering is only as good as the access rules attached to each chunk. Getting those right at ingestion is where most of the work is.

Read the real rule, not the folder name. File shares and document libraries have inheritance, broken inheritance, nested groups, sharing links and individual exceptions. A folder called "Commercial – Restricted" may contain a subfolder somebody shared with a whole project team two years ago. The ingestion job must read the effective permissions of each document from the source system, not infer them from where it sits.

Resolve groups the way the source does. If the source grants access to a group that contains other groups, the index needs either the expanded membership or a way to evaluate group membership at query time. Getting this wrong in the permissive direction is a leak; getting it wrong in the restrictive direction quietly removes content from people who need it, and nobody complains because they never knew it was there.

Treat derived content as content. This was Bergvliet's worst finding. To make rate questions easier, the pilot generated short summaries of each rate schedule and indexed those too. The summaries were written to a general folder and inherited its permissions, not the schedules'. The restricted figures were now readable by everyone, one step removed. Any summary, extracted table, translated version or generated answer stored for reuse must carry the most restrictive rule of everything it was built from.

Respect boundaries that are not groups. The joint-venture documents were governed by an agreement, not just by a group: they could be used for that bid and not for future Bergvliet bids, even by people who had worked on it. Bergvliet put them in a separate index the main Bid Library never queries. Sometimes the right permission design is a separate collection rather than a cleverer filter.

The index is always a little out of date

Source permissions change. Someone joins the commercial team, someone leaves the firm, a schedule is moved to a restricted folder after a pricing dispute. The index learns about it on the next sync.

The exposure window after a permission changeTIME THE INDEX SHOWS THE OLD PERMISSIONNightly syncup to 12 hours, or 24 if a run failsChange event +query-time checkminutes0 h6 h12 h18 h24 h
Illustrative timings. When the commercial team restricts a rate schedule at the source, a nightly sync leaves the old permission in the index for up to a day. Propagating the change as an event, and rechecking permission at query time, shrinks the window to minutes.

Bergvliet's pilot synced nightly. That meant a permission removed at nine in the morning stayed effective in the index until the evening run, and for a full day if that run failed, which it occasionally did. For most content that window is tolerable. For restricted pricing, or for a person who has just been dismissed, it is not.

The design the team settled on has two layers:

  1. Propagate permission changes as events. When the source system reports a permission change, the affected chunks are updated within minutes, separately from the slower content re-indexing.
  2. Recheck at query time for sensitive collections. For the restricted collections, the "check" stage of the pipeline (Part 3) confirms each surviving chunk's permission against the source, or a fast permission cache that is invalidated by those events, before anything reaches the model.

The query-time check costs a little latency. It is cheap compared with explaining why a dismissed employee could still query pricing for an afternoon.

Identity has to be the caller's, all the way down

A recurring mistake is a pipeline that authenticates the user at the front door and then queries the index with its own service identity. Every permission filter then evaluates the service's rights, which are usually "everything".

The caller's identity, or a token that carries it, must reach the retrieval step on every path: the first question, the follow-up, the "search again" button, the scheduled digest, and any tool an agent calls on the user's behalf. When an agent searches, it searches as the person it is working for, never as itself. The same applies to caches: a cached result is keyed on the question and the caller's effective permissions, or it is not cached at all for restricted collections.

Prove it with paired tests

Permission-aware retrieval is a security control, so it needs tests that try to break it, not just tests that show it working.

Bergvliet's approach is simple and worth copying. For each restricted collection, the team keeps pairs of test identities: one that should see the content and one that should not. A set of probe questions, written to match restricted content as strongly as possible, runs as both identities on every change to the pipeline, the chunking rules or the permission sync. The test fails if:

  • the restricted identity receives any chunk, snippet, citation or title from the restricted collection;
  • the result count, suggestions or timing differ in a way that reveals restricted content exists;
  • the permitted identity does not receive it, which catches over-restriction;
  • a cached answer crosses from one identity to the other.

Twenty probes run in under a minute. They are part of the evaluation set in Part 7, and a failure blocks release.

Log the decision, not just the answer

When someone later asks "could this person have seen that schedule?", the answer must come from evidence, not memory. For each query, Bergvliet records the caller, the permission set applied, the chunks retrieved and the chunks removed by the query-time check. It does not record the text of restricted chunks in the log, only their identifiers, so the log does not become a second unprotected copy. For South African organisations, the same thinking applies to personal information under POPIA's security safeguards condition; AI and POPIA in plain language covers the wider questions.

Key points

Same rules, every query, at query time. The index must enforce the source system's permissions, not its own.

Pre-filter, do not post-filter. Post-filtering degrades answers and leaks through counts, snippets, suggestions and caches.

Derived content inherits the strictest rule. Summaries, extracts and translations are where permissions quietly get lost.

Close the sync gap for sensitive content. Event-driven updates plus a query-time check shrink exposure from hours to minutes.

Test with paired identities. Probe questions that should be refused, run on every change, are what make the control real.

Next: Part 6, Reranking, context budgets and cost. All parts are on the series page.

How CloudNala can help

We review how RAG systems capture, synchronise and enforce document permissions, including derived content, caches and agent tool paths, and we build the paired-identity probe tests that prove isolation on every release. The aim is a knowledge assistant your security and compliance people can sign off with evidence rather than assurances.


Work with CloudNala

CloudNala helps organisations move from technology ambition to practical execution across cloud, AI, data, platform engineering and digital services.

Whether you are exploring AI, modernising your cloud environment, building a public-sector digital service, or turning an idea into a working MVP, we can help you shape the roadmap and deliver the next step.

Book a RAG Production Review or write to us at consult@cloudnala.co.za