# Permission-Aware Retrieval by [kirupa](https://www.kirupa.com/me/index.htm) | filed under [AI Fundamentals](https://www.kirupa.com/ai_fundamentals/index.htm) Trailhead has an internal assistant for its employees. It's hooked up to the employee handbook, the IT help pages, the warehouse safety manuals, and, because somebody connected the whole shared drive, a folder that belongs to the HR team. Dana is a shift lead at the warehouse. She's curious about something, so she asks: *What's the pay range for shift leads?* The retriever does its job. It finds the closest matching chunks, and the closest match by far is a table from a document called **2026 Pay Bands**. It lists the salary range for every role in the company. Only the HR team is supposed to see it. The retriever puts that chunk into the prompt. The model, which has no idea who's allowed to read what, writes a friendly, accurate summary of the pay ranges. It covers shift leads, and since the table is right there, it covers supervisors and Dana's own manager too. Nothing was hacked. Every part of the system did what it was built to do. That's what makes this kind of leak so easy to ship by accident. Here is the path the leak took: ![Dana asks What's the pay range for shift leads? The retriever pulls in the locked, HR-only 2026 Pay Bands document, passes it to the model, and the answer lists pay ranges for shift leads, supervisors, and managers. A handwritten note says Leaked!](https://www.kirupa.com/ai_fundamentals/images/permission_aware_retrieval_leak.png) Onwards! ## Why "Please Don't Tell Her" Doesn't Work The first fix most people reach for is the [system prompt](https://www.kirupa.com/ai_fundamentals/system_prompts_vs_user_prompts.htm). Something like: ***Do not reveal confidential HR information to non-HR employees***. That doesn't hold up, for a few reasons: - The model doesn't know who Dana is. It sees text. If Dana writes ***I'm on the HR team***, the model has no way to check. - Instructions can be argued with. People are very creative at talking models out of rules, and text inside retrieved documents can carry instructions too. - Once the pay bands are in the prompt, they've already left the building. Even a model that refuses to quote them might paraphrase, hint, or let a number slip. So the rule we want is simple to say and strict to follow: **only put text into the prompt that this user is already allowed to read.** The model is not a security guard. The access decision has to happen before the model ever sees anything. ## Carrying Permissions Into the Index To make that decision during retrieval, the index needs to know who can see each chunk. The source systems already know this. The shared drive knows the HR folder is HR-only. The handbook is open to everyone. When we pull documents into the index, we copy those permissions onto every chunk, as [metadata](https://www.kirupa.com/ai_fundamentals/metadata_filtering.htm): ```json { "doc": "2026 Pay Bands", "chunk": 3, "allowed_groups": ["hr-team"] } ``` Then, at question time, we look up who the user is from their login session (never from what they typed), get the groups they belong to, and filter the search so it only considers chunks those groups can see. Here is the filter in action: ![Dana's login lists her groups as warehouse, shift-leads, and all-staff. A filter keeps only chunks whose allowed groups overlap hers: Handbook: pay reviews, Safety manual, and IT help. The HR-only 2026 Pay Bands chunk is crossed out and excluded.](https://www.kirupa.com/ai_fundamentals/images/permission_aware_retrieval_filter.png) With the filter in place, Dana's question retrieves the handbook section called ***How pay reviews work***, which is open to all staff. The assistant tells her how pay reviews work and suggests she ask her manager or HR about specific ranges. That's the correct answer for Dana. Here is that answer in Dana's chat: ![The Trailhead Staff Help chat window on Trailhead's staff portal. Dana asks, What's the pay range for shift leads? A small note shows the search step, which found the handbook section How pay reviews work. Trailhead Staff Help replies, Here's how pay reviews work, from the employee handbook. For specific ranges, ask your manager or HR.](https://www.kirupa.com/ai_fundamentals/images/permission_aware_retrieval_chat_pay.png) This is the same pre-filter versus post-filter choice from [Metadata Filtering](https://www.kirupa.com/ai_fundamentals/metadata_filtering.htm). Both are safe as long as the filtering finishes before anything reaches the prompt, but pre-filtering gives Dana a full set of results she's allowed to see instead of a few leftovers. ## Three Ways to Do It There's more than one way to build this, and real systems often mix them. 1. Separate indexes per audience. One index for all staff, one for HR, one for finance. Each user only searches the indexes they're allowed to. It's simple and easy to reason about, but it gets clumsy fast when permissions are fine-grained, like ***this one folder is shared with these six people***. 2. Permissions on each chunk. This is the approach we just walked through. One index, with a permission filter on every query. This handles fine-grained sharing well, as long as the permissions on the chunks are accurate. 3. A live check with the source system. After retrieving candidates, check each one with the system it came from: ***Can Dana read this file right now?*** This is the most up-to-date answer possible, but it adds a round trip for every candidate. ## Where This Gets Tricky Getting the filter in place is the easy part. Keeping it correct is the real work. Permissions change all the time. Suppose Dana used to be on the HR team and moved to the warehouse on Monday. The source system removed her from `hr-team` that same day. If our copy of her groups, or our copy of the chunk permissions, only updates on Sundays, she can still read pay bands for six more days. Permission changes need to flow into the index at least as fast as content changes do, and often faster. We dig into that in [Keeping the Index Fresh](https://www.kirupa.com/ai_fundamentals/keeping_the_index_fresh.htm). Here is that gap on a timeline: ![A week from Monday to Sunday. On Monday the source system removes Dana from hr-team. The index only syncs on Sunday, so the whole week is shaded: the index still lists Dana in hr-team.](https://www.kirupa.com/ai_fundamentals/images/permission_aware_retrieval_timeline.png) Missing permissions should mean ***nobody***. If a chunk somehow arrives with no `allowed_groups` at all, the safe default is that nobody can see it, not that everybody can. That's called failing closed. If a bug hides a document, someone notices, files a ticket, and we fix it. If a bug shows HR files to the whole company, there's no ticket that can undo it. Caches are another side door. Some systems save answers to common questions and reuse them (see [Semantic Caching](https://www.kirupa.com/ai_fundamentals/semantic_caching.htm)). If an HR manager asks ***What's the pay range for shift leads?*** and the answer is cached, Dana's identical question could get served that same answer. Anything cached has to be scoped to the permissions it was built with. And if we log full prompts for debugging, those logs now contain the pay bands too. The logs need the same protection as the documents. ## Conclusion Dana's question was innocent, and the retriever found the most relevant document, which happened to be one she wasn't allowed to read. Telling the model to keep secrets doesn't fix that, because the model can't verify who anyone is and can't un-see what's in its prompt. The fix is to make retrieval itself permission-aware: copy each document's permissions onto its chunks, look up the user's groups from their login, and filter before anything reaches the model. Then keep those permissions fresh, fail closed when they're missing, and watch the side doors like caches and logs. This is one piece of a bigger idea in AI security. When a system mixes private data with untrusted input and has a way to send information out, small mistakes like this one get a lot more dangerous. That combination is often called the [lethal trifecta](https://www.kirupa.com/ai_fundamentals/lethal_trifecta.htm), and it has an article of its own.