The moment your product serves more than one customer over shared infrastructure, you have a multi-tenancy problem — and in a knowledge base product, the stakes are unusually high. A bug that lets Tenant A's query retrieve Tenant B's documents isn't a glitch; it's a data breach. Getting isolation right is foundational, and it's expensive to change later, so it's worth being deliberate about up front.

The isolation spectrum
Multi-tenant data isolation runs along a spectrum from "share everything" to "share nothing," and the choice is a trade-off between isolation strength and operational cost.
Shared table, tenant column (pooled). All tenants' data lives in the same tables, tagged with a tenant_id (or org_id) column. Every query filters on it. This is the most resource-efficient model — one schema, one connection pool, trivial to add a tenant — and the most operationally simple. Its weakness is that isolation depends entirely on never forgetting the filter. One query missing its WHERE org_id = ... and a tenant sees another's data.
Separate schema per tenant. Each tenant gets its own schema within a shared database. Stronger isolation — a query is scoped to a schema — at the cost of schema sprawl and migrations that now have to run across N schemas.
Separate database per tenant (siloed). Each tenant gets a physically separate database. The strongest isolation: there's no shared table for a bad query to reach across, and you can even locate a tenant's data in a specific region for compliance. The cost is operational — provisioning, connection management, and migrations multiply per tenant.
| Pooled (shared table) | Schema-per-tenant | Database-per-tenant | |
|---|---|---|---|
| Isolation strength | Depends on query discipline | Strong | Strongest |
| Cost per tenant | Lowest | Moderate | Highest |
| Onboarding a tenant | Instant | Create schema | Provision database |
| Blast radius of a bug | All tenants | One schema | One tenant |
| Per-region data residency | Hard | Hard | Natural |
There's no universally right answer — but for a knowledge base, where the data is the product and a cross-tenant leak is a breach, the calculus leans toward stronger isolation than a generic CRUD app would need.
Enforce isolation in code, not just convention
If you use the pooled model, "remember to filter" is not a security strategy — humans forget, and the one query that forgets is the incident. Isolation has to be enforced structurally so that forgetting is impossible, not merely discouraged.
The pattern is to route every query through a layer that injects the tenant scope automatically, so no hand-written WHERE clause is the thing standing between tenants. In Kognita's backend, the ORM's privacy layer filters every query by the org on the request context — you set an org viewer on the context once, and the data layer refuses to return rows outside that org. The application code cannot accidentally query across tenants because the scoping isn't its responsibility to remember; it's enforced beneath it.
// The org scope is set once, on the context...
ctx = viewer.NewContext(ctx, viewer.NewOrgViewer(orgID))
// ...and every subsequent query is automatically constrained to that org.
// A query that "forgets" to filter still can't cross the tenant boundary.
The principle generalizes beyond any one framework: make the safe path the only path. Whatever your stack, the tenant filter should be applied by infrastructure the feature code can't bypass, not by a convention the feature code is trusted to follow.
The vector dimension of the problem
Knowledge base products have a wrinkle a plain SaaS app doesn't: the vectors. Your embeddings live in a vector store, and that store needs the same tenant isolation as your relational data. A leak is a leak whether it happens in Postgres or in the vector index.
This is where the architecture choice compounds. Kognita provisions a separate vector database per organization — each org gets its own Neon project, with its own connection string, provisioned at signup. That's the database-per-tenant model applied specifically to the highest-risk data. One org's embeddings physically cannot appear in another org's search results, because they're not in the same database to begin with. The relational metadata can stay pooled-with-enforcement while the vectors — the crown jewels — get hard physical isolation. You don't have to pick one model for the whole system; you can match the isolation strength to the sensitivity of each data type.
Onboarding and provisioning
Whatever isolation model you pick, tenant onboarding has to be automated — the day you have fifty tenants, no one is hand-creating databases. This means a provisioning flow that runs at signup: create the tenant's isolated resources (schema, database, vector store), store the connection details, and wire them into the request path.
Two things make this robust:
- Provisioning is a saga, not a single step. Creating a per-tenant database, waiting for it to come up, running migrations, and recording its connection string can each fail independently. Treat onboarding as a sequence with defined retry and rollback, so a half-provisioned tenant doesn't become a silent landmine.
- Connection routing is dynamic. With per-tenant databases, the app resolves which database to talk to per request from the tenant on the context. A connection manager keyed by tenant ID, resolving lazily and caching, keeps this efficient without a fixed pool per tenant.
Practical guidance
- Default to enforced isolation over trusted isolation. Whatever model you choose, make the tenant boundary something your feature code can't cross even by accident. Convention-based filtering is a breach waiting for its first forgotten
WHERE. - Match isolation strength to data sensitivity. Pooled-with-enforcement is fine for low-risk metadata; the actual knowledge — documents, embeddings — deserves stronger isolation. Mixing models is not a compromise, it's good design.
- Automate onboarding from day one. Manual provisioning works at three tenants and collapses at thirty. Build the saga before you need it.
- Decide early. Isolation is the hardest thing to change after launch, because it's woven through every query and every table. Pay the design cost up front rather than the migration cost later.
Multi-tenancy is where a knowledge base SaaS either earns customer trust or loses it in a single incident. The teams that get it right treat isolation not as a feature to add but as the substrate everything else is built on — enforced by infrastructure, matched to data sensitivity, and never left to the memory of whoever wrote the last query.