cd /news/artificial-intelligence/permission-aware-rag-enforcing-docum… · home topics artificial-intelligence article
[ARTICLE · art-75134] src=dev.to ↗ pub= topic=artificial-intelligence verified=true sentiment=· neutral

Permission-Aware RAG: Enforcing Document ACLs at Retrieval Time

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.

read2 min views1 publishedJul 27, 2026

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.

When 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.

Generate 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.

Retrieve 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.

In Atlas, every chunk in pgvector carries a clearance label (public | analyst | compliance | restricted

) 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):

SELECT id, document_id, content, clearance, metadata,
       (1 - (embedding <=> ?::vector)) AS score
FROM atlas_chunk
WHERE clearance = ANY(?)        -- caller's visible labels, enforced BEFORE ranking
ORDER BY embedding <=> ?::vector
LIMIT ?;

One detail worth copying: both retrieval paths — dense kNN and sparse full-text — build their WHERE

clause 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.

The 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.

A 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.

Two implementation notes that mattered:

clearance=SECRET

as 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.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @atlas 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/permission-aware-rag…] indexed:0 read:2min 2026-07-27 ·