Back to blog
6 min readGateco Team

Why pgvector RLS Gets You to Ten Tenants, and What Breaks After That

When teams ask "how do we add access control to our RAG pipeline?", the first answer is almost always the same: pgvector + Supabase + Row Level Security. It is a reasonable starting point. Postgres is the right database for many early RAG deployments, RLS is built in, and the pattern works well enough to ship a first version. The problem is not that RLS is wrong — it is that it works until it doesn't, and the triggers that break it are predictable.

What pgvector RLS actually does

RLS is a Postgres feature that adds a `USING` predicate to every query against a table. A simple department-based policy looks like this: `CREATE POLICY department_access ON embeddings USING (department = (SELECT department FROM users WHERE id = auth.uid()))`. Every vector similarity query automatically joins to the users table and filters by department. For single-database, same-tenant deployments where principals and their attributes live in the same Postgres schema, this works cleanly and with minimal overhead.

The five triggers

The triggers that push teams beyond RLS are consistent. The first is a second vector database. When you add Pinecone for semantic search or Qdrant for a new use case, RLS does not travel — it is a per-Postgres-instance feature. You now have two authorization systems that need to stay in sync. The second trigger is an audit request. "Who accessed what through the AI last quarter?" is a common security team question. pgvector has no authorization-level logging — you would need to build it in your application layer and hope nothing slips through. The third trigger is IDP sync. Your principals and their department or role attributes live in Azure AD, not in a local users table. Syncing that data into Postgres and keeping it current requires building and maintaining a sync pipeline. The fourth trigger is an EU AI Act or SOC 2 deliverable. Compliance teams need documented access control policies — not SQL predicates that live in a migration file. The fifth trigger is instant revocation. When an access rule changes, RLS changes require a schema migration. There is no "deactivate this policy now" operation.

The code comparison

The Supabase RLS policy above covers the happy path cleanly. The divergence appears at the first edge case: what happens when the `department` field is in Okta, not a local table? What happens when you need ABAC conditions like `clearance_level >= 3`? What happens when you add a second vector DB? Each of these requires extending the RLS policy, adding new tables, and building new sync logic. The equivalent Gateco call is: `results = client.retrieve(connector_id="my-connector", query="...", principal_id="user-123")`. The principal's department is resolved from the synced IDP, the policy is evaluated against the chunk's classification, and the decision is logged — regardless of which vector DB serves the query.

When pgvector RLS is enough

This is worth stating clearly: RLS is the right choice when the constraints match. If you use a single Postgres-based vector database (pgvector, Supabase, Neon), your principals and their attributes live in the same Postgres schema, you have no cross-cloud or multi-DB retrieval requirements, no SSO/SCIM requirements, no audit-export deliverables, and your team has strong Postgres expertise and can maintain the schema over time — RLS is a perfectly valid, low-overhead authorization mechanism. Do not add infrastructure you do not need. The [full comparison](/compare/pgvector-rls) shows the capability matrix in detail.

Migration is additive

If you are already running pgvector RLS and hitting the triggers above, the migration path is additive. Connect your pgvector instance to Gateco as a connector, define your policies in the Policy Studio, and run the Access Simulator to validate before switching over. Your existing RLS policies can stay in place during the transition — they are evaluated before Gateco's policy layer. Once you have confidence in the Gateco policies, you can remove the RLS predicates from your schema.


Ready to secure your AI retrieval?

Start with the free tier — 100 retrievals/month, no credit card required.