{"slug": "permission-aware-rag-enforcing-document-acls-at-retrieval-time", "title": "Permission-Aware RAG: Enforcing Document ACLs at Retrieval Time", "summary": "A developer building Atlas, an enterprise copilot for a compliance domain, implemented permission-aware RAG by embedding clearance labels into pgvector chunks and filtering by the caller's verified clearance before ranking. This approach makes cross-clearance leakage structurally impossible, and a negative-access hard gate in CI ensures the build fails on any leakage.", "body_md": "Most RAG demos have a security model of \"none.\" Documents go into one shared index; anyone who can ask a question can surface content from any document. In a compliance or financial domain, that's not a rough edge — it's a disqualifier.\n\nWhen I built Atlas, an enterprise copilot for a compliance domain, the first architectural decision was where access control lives. There are three options, and two of them are wrong.\n\nGenerate the answer, then check whether the user was allowed to see the sources. By then the LLM has already read the restricted content and may have leaked it through paraphrase, summary, or even its refusal (\"I can't tell you about the Q3 restructuring memo…\" is itself a leak). Post-hoc filtering treats a security boundary like a UX problem.\n\nRetrieve top-k from the shared index, then drop chunks the user can't access. Better, but it corrupts retrieval quality: your top-k gets silently thinned, and ranking scores were computed against a corpus the user shouldn't see. Worse, timing and scoring side-channels can reveal that restricted documents exist.\n\nIn Atlas, every chunk in pgvector carries a clearance label (`public | analyst | compliance | restricted`\n\n) as metadata, written at ingestion. Retrieval filters by the caller's *verified* clearance **before** ranking. This is the actual shape of the dense retrieval query (sparse tsvector search shares the same predicate builder):\n\n``` js\nSELECT id, document_id, content, clearance, metadata,\n       (1 - (embedding <=> ?::vector)) AS score\nFROM atlas_chunk\nWHERE clearance = ANY(?)        -- caller's visible labels, enforced BEFORE ranking\nORDER BY embedding <=> ?::vector\nLIMIT ?;\n```\n\nOne detail worth copying: both retrieval paths — dense kNN and sparse full-text — build their `WHERE`\n\nclause from a **single shared RBAC filter builder**, so there is exactly one trust boundary and it cannot be bypassed. The predicate is pushed into SQL, never applied as an optional post-filter.\n\nThe property this buys you: cross-clearance leakage becomes *structurally impossible*, not \"tested for.\" The restricted document isn't ranked lower or filtered out — from the query's perspective, it does not exist.\n\nA design is worth little without a regression gate. Atlas has a **negative-access hard gate** in CI: an eval suite where low-clearance users ask questions whose only correct sources are restricted documents. The passing behavior is a grounded refusal — and the gate fails the build on any leakage. It runs GPU-free against committed cassettes, so it gates every merge like a unit test.\n\nTwo implementation notes that mattered:\n\n`clearance=SECRET`\n\nas a parameter owns your corpus.)Ten years of backend work taught me that authorization belongs in the data path, not bolted on after. RAG doesn't change that rule; it just gives you a new data path to forget it in. Put the ACL in the retrieval predicate, then write the eval that fails your build when someone breaks it.", "url": "https://wpnews.pro/news/permission-aware-rag-enforcing-document-acls-at-retrieval-time", "canonical_source": "https://dev.to/venkathub/permission-aware-rag-enforcing-document-acls-at-retrieval-time-54a7", "published_at": "2026-07-27 08:40:19+00:00", "updated_at": "2026-07-27 09:06:59.878435+00:00", "lang": "en", "topics": ["artificial-intelligence", "large-language-models", "ai-safety", "ai-products", "developer-tools"], "entities": ["Atlas"], "alternates": {"html": "https://wpnews.pro/news/permission-aware-rag-enforcing-document-acls-at-retrieval-time", "markdown": "https://wpnews.pro/news/permission-aware-rag-enforcing-document-acls-at-retrieval-time.md", "text": "https://wpnews.pro/news/permission-aware-rag-enforcing-document-acls-at-retrieval-time.txt", "jsonld": "https://wpnews.pro/news/permission-aware-rag-enforcing-document-acls-at-retrieval-time.jsonld"}}