Build · Advanced

AI assistants and document permissions: who may get the answer?

An internal AI assistant that searches company documents is only as discreet as its retrieval layer. This guide shows how to make AI assistant document permissions hold: who may read what is decided before any text reaches the model, and it is tested like any other security control.

7 min read

Three combination padlocks on a chain around a metal box

Photo: Nilo Velez, public domain (CC0)

In this guide

The root problem: one service account that reads everything

A retrieval assistant works in two stages. An indexer reads the company’s documents, cuts them into passages and stores them in a search index. When someone asks a question, the assistant searches that index, passes the best passages to a language model and returns the model’s answer.

The indexer normally runs under a service account with read access to everything, because that is the easiest way to get a complete index. If nothing else is done, the assistant then answers every employee with the rights of that service account. Folder permissions are bypassed by a chat window: the salary list or one customer’s contract is a well-phrased question away.

This is a classic “confused deputy”: a component with broad rights acts for a user with narrow rights and does not check the difference. Broad read access for the indexer is acceptable. Answering with it is not.

Enforce document permissions at retrieval time, with the asker’s identity

The rule that makes an assistant safe is short: a passage the asking user may not read must never enter the model’s context.

  • Store permissions with every passage. Each indexed passage carries the ID of its source document, the users and groups allowed to read it, any explicit denials, and a tenant ID where several customers share the system.
  • Take the identity from the authenticated session. The retrieval service receives the user’s verified identity from the login token. It never accepts a user ID from a form field, from the prompt, or from arguments the model itself produced.
  • Filter inside the search. The permission condition is part of the search query, so the index only returns passages the user may read. Filtering a finished top-ten list afterwards gives poorer answers, because permitted passages ranked eleventh and below are never considered.
  • Never filter after the model. Asking the model to “leave out confidential content”, or scanning its answer, is not access control. Once the text is in the context, a paraphrase, a summary or a single figure can carry it out.

Metadata leaks too. A document title in a list of sources, a count (“I found 3 documents I cannot show you”) or a file path reveals that something exists. Apply the same filter to citations, suggestions and autocomplete.

Keeping permissions in the index in step with the source

The index holds a copy of the permissions, and copies go stale. Three details decide whether they stay correct.

First, a permission change does not change the document. An indexer that only re-reads files whose content or modification date changed will miss it. Permissions need their own synchronisation: change events from the source system where they exist, and a regular full comparison as a safety net.

Second, store groups, not expanded member lists. If a passage is marked “readable by the group Finance” and the user’s group memberships are fetched from the identity provider when the question is asked, removing a person from Finance takes effect at once, with no re-indexing. If the index stores the names of all Finance members, every staffing change means rewriting thousands of entries.

Third, permissions are often inherited. Moving a document into another folder, or changing a folder’s rights, changes the effective rights of everything beneath it. The sync must compute effective permissions, not only those set on the file itself. Deleted documents must leave the index, and the team needs a rule for “anyone with the link” sharing: the safe default is to treat such documents as readable only by people named explicitly.

For the most sensitive sources, add a second check: before the top passages go to the model, ask the source system whether this user may read those documents now. It costs some latency and removes the dependence on the copy.

Cached answers, conversation history and revoked access

Leak paths in a retrieval assistant and the safeguard for each
Leak path How it leaks Safeguard
Shared answer cache A’s answer is served to B who asks the same question No answer cache across users, or key it by the user’s permission set
Stale permissions in the index Access removed at the source, passage still returned Permission sync by change events plus a regular full comparison
Conversation history Earlier restricted text is reused as context after revocation Re-check sources of earlier turns, or expire history
Shared or exported chats A conversation link gives colleagues the quoted text Sharing applies the recipient’s permissions, or is switched off
Logs and evaluation sets Prompts with retrieved passages readable by a wide team Restrict access, shorten retention, store document IDs instead of text
Training on documents Content becomes part of the model with no permissions attached Do not fine-tune on restricted documents: retrieve instead
Sources and counts Titles or totals reveal that a document exists Filter citations and suggestions with the same rule

An answer is derived from documents, so it inherits their restrictions. The simple rule: an answer may be reused only for a user whose permissions cover every source it was built from.

Conversation history is subtler. A user who read an answer last week has seen it, just as they could have downloaded the file. What must not happen is that old turns keep feeding restricted text into new answers, or that a memory feature carries it into later sessions. Store the source document IDs with each answer so they can be re-checked.

How quickly must a revocation take effect? Set a number and write it down. The figures below are an illustrative design comparison, not measurements: the longest time, in minutes, that a removed right could still produce an answer under four approaches.

Illustrative example: worst-case delay before a revoked right takes effect

A nightly run means up to 24 hours (24 x 60 = 1,440 minutes), rarely acceptable for a leaver. A sensible target combines the last two: group membership and account status checked on every question, document-level changes applied within minutes.

Testing for leaks between employees and between customers

Permission handling in an AI assistant is a security control and needs tests that run automatically, after every deployment and on a schedule.

  1. Create two test users with different rights, for example one in the Finance group and one outside it.
  2. Place canary documents in restricted locations. Each contains a made-up fact and a unique string that appears nowhere else, such as an invented project code.
  3. Ask both users the same questions about the canary facts, in direct and indirect wording. The permitted user must get the answer with its source. The other must get nothing: no fact, no title, no hint that a document exists.
  4. Search the second user’s answers, citations and suggestions for the canary strings. One match fails the build.
  5. Test revocation: grant access, ask, remove access, then ask repeatedly and measure how long the answer keeps coming back. Compare the result with your target.
  6. Repeat the question in the same conversation after revocation, and from a second account straight after the first, to exercise history and caches.
  7. Where several customers share the system, run the same tests across tenants with a canary per customer.

Between customers, a filter on a tenant ID is the minimum, and it must be applied by the retrieval service itself, not chosen by the calling code. A separate index per customer is the stronger design, because a missing filter then returns nothing instead of everything. The same thinking applies to a customer portal that offers search or an assistant, and it belongs in the regular review of security and backups.

Questions we hear often

No. The model has no reliable knowledge of who is asking and can be led to reveal text it was given. Access must be decided by the retrieval layer before the model sees anything.

The user has already seen those answers, so the practical goal is that the text is not reused. Store source IDs with each answer, re-check them before old turns are used as context, and set a retention period for history.

Not always, but it is the safer default for confidential data. With a shared index, isolation depends on a filter being applied correctly on every query, so it needs the cross-tenant tests described above.

Want this sorted without doing it yourself?

Tell us what you are working on. A person replies within one working day.