Retrieval augmented generation collapses the distance between a question and every document behind it. In an enterprise that includes compensation tables, incident reports, and board decks. The failure mode is not a bad answer; it is a correct answer built from a document the caller was never allowed to read.
The control belongs at retrieval
The only durable place to enforce access is the retrieval step. If an unauthorized chunk reaches the prompt, every downstream control is a mitigation rather than a boundary. Attach a security predicate to the query and let the vector store do the filtering.
const results = await qdrant.search('knowledge', {
vector: embedding,
limit: 8,
filter: {
must: [
{ key: 'tenant_id', match: { value: tenantId } },
{ key: 'acl', match: { any: callerRoles } },
],
},
});Where teams get it wrong
| Pattern | Failure mode | Fix |
|---|---|---|
| Post-filter in application code | Over-fetch then discard, leaks in logs | Filter inside the query |
| Prompt-level instructions | Model may ignore or summarize protected text | Enforce in retrieval |
| Per-document ACL only | Graph edges bypass document rules | Permission nodes and edges |
GraphRAG needs dual-level permissions
Graph retrieval traverses relationships. A node may be visible while the edge that connects it encodes a sensitive relationship. Apply the same security predicate to node lookups and to edge traversals, and audit both.