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.

ts
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

PatternFailure modeFix
Post-filter in application codeOver-fetch then discard, leaks in logsFilter inside the query
Prompt-level instructionsModel may ignore or summarize protected textEnforce in retrieval
Per-document ACL onlyGraph edges bypass document rulesPermission 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.