In a multi-tenant product, every vector row must carry its tenant and its document ACL, and every query must be filtered by the caller's real identity — derived server-side, never from model output or client input.
sql -- pgvector: filter by tenant and permitted groups, then rank SELECT id, text, embedding <=> $1 AS distance FROM chunks WHERE tenant_id = $2 AND acl_groups && $3::text[] -- caller's group memberships ORDER BY embedding <=> $1 LIMIT 50;
Additional controls: - Enforce row-level security in the database so a missing WHERE clause fails closed. - Re-check permissions at answer time for cited documents — ACLs change between ingest and query. - Redact secrets and PII during ingestion; an embedded API key is still an exposed API key. - Delete pipeline: removing a document must remove its chunks, its vectors, and any cached answer referencing it.
Gotcha: vectors are not anonymised. Embedding inversion can reconstruct meaningful fragments of source text, so treat the vector store with the same sensitivity as the documents.