A RAG system that retrieves documents across permission boundaries is a data leak waiting to happen. An HR agent that answers a question about engineering salaries, a support agent that exposes internal architecture documents, a patient-facing agent that reveals other patients' medical records — these are all permission failures in RAG. Document-level access control is not optional for production RAG. It is a compliance requirement.
The Permission Model
Document-level permissions follow the same RBAC model as application permissions: each document chunk carries a permission tag (role, department, classification level, tenant), each user carries a set of permissions, and the retrieval query filters chunks by the user's permissions before semantic search. The permission check happens at the database level (PostgreSQL row-level security) or the application level (post-retrieval filtering). Database-level enforcement is more secure — it cannot be bypassed by application bugs.
Implementation in PostgreSQL
PostgreSQL row-level security (RLS) provides database-level permission enforcement for RAG. Tag each chunk with a permissions column (e.g., department, classification_level). Create an RLS policy that compares the user's permissions against the chunk's permissions. The retrieval query automatically filters chunks the user is not authorised to see. The user's permissions are set via a session variable at connection time. This approach is secure, performant, and cannot be bypassed by application code.
Warning
Always set the user's permissions at the database session level, not the application level. Application-level filtering can be bypassed by bugs or direct database access.
Performance Considerations
Permission filtering adds overhead to retrieval queries: each query must check permissions before or during semantic search. Optimise with: composite indexes on (permissions, embedding) columns, pre-filtering (filter by permissions first, then search within the filtered set), and caching (cache permission-filtered result sets for repeated queries). The overhead is typically 10-30% — acceptable for the security guarantee. The alternative (no permission filtering) is a compliance violation.
Conclusion
RAG permissions are the same principle as application permissions, applied to document retrieval. Database-level enforcement (PostgreSQL RLS) is the most secure approach. The performance overhead is acceptable — the compliance guarantee is not optional.
Key Takeaways
- Document-level access control is a compliance requirement — not a feature
- Tag every chunk with permissions; filter by user permissions at query time
- PostgreSQL RLS provides database-level enforcement — cannot be bypassed by application bugs
- Performance overhead: 10-30% — acceptable for the security guarantee
- The alternative (no permission filtering) is a compliance violation