Three patterns, three risk profiles
1. Collection-per-tenant
Each tenant gets their own Chroma collection or pgvector table. Strongest isolation; can become operationally heavy with thousands of tenants.
2. Shared collection + tenant_id metadata
One big collection, every chunk has metadata.tenant_id, every query includes where={tenant_id}. Cheap, scales to millions of tenants, but a missing filter leaks across tenants. Test rigorously.
3. Schema-per-tenant (pgvector)
Use Postgres schemas to namespace tenants. Single connection pool, but cleaner isolation than the metadata pattern. Use search_path to scope queries automatically.
The contract that prevents leaks
Wrap retrieval in a function that requires a tenant_id parameter. No code path can call the retriever without one. The compiler enforces what code review will eventually miss.