tenant-isolation-models-reference
Pure-reference catalog of tenant-isolation models for B2B SaaS. Defines the isolation continuum from full-isolation (separate compute + data + network per tenant) to fully-shared (one deployment, tenant_id discriminator), names the canonical models (Microsoft's automated-single-tenant / fully-multitenant / vertically-partitioned / horizontally-partitioned; AWS Well-Architected's silo / pool / bridge framing; deployment-stamps / supertenants terminology), enumerates the trade-offs (cost, blast radius, noisy neighbor, compliance, scale limits), and lists the test surfaces each model creates (cross-tenant data leak, tenant-id propagation, deployment-routing). Use as the model-selection reference when designing or auditing tenant isolation. Consumed by tenant-leak-test-author, cross-tenant-data-leak-tests.
Install with skills.sh (any agent)
npx skills add testland/qa --skill tenant-isolation-models-referencetenant-isolation-models-reference
Overview
Tenant isolation is the foundational concern of every B2B SaaS architecture: the AWS Well-Architected SaaS Lens calls it essential and treats crossing a tenant boundary as a significant, potentially unrecoverable event for a SaaS business. Isolation is a continuum, not a binary - Microsoft's Azure Architecture Center frames it as a spectrum from shared-nothing to everything-shared, with architectures often picking different points per tier (UI shared, app shared, data isolated). This skill is a pure reference consumed by the per-model test authors and the tenant-leak critic; it executes nothing.
When to use
Tenant vs deployment
A tenant is a logical customer boundary; a deployment (also called a stamp or supertenant) is a physical set of infrastructure. One deployment can host many tenants (shared model), or each tenant can have its own deployment (silo). The tenant-to-deployment mapping is durable state: a routing table must exist somewhere so requests reach the right deployment.
The four canonical models
| Model | Compute | Data | Network | Cost/tenant | Blast radius | Noisy neighbor |
|---|---|---|---|---|---|---|
| Automated single-tenant (silo) | Dedicated | Dedicated | Dedicated | Highest | One tenant | None |
| Fully multitenant (pool) | Shared | Shared (tenant_id discriminator) | Shared | Lowest | All tenants | High |
| Horizontally partitioned (bridge) | Shared | Dedicated per tenant | Shared | Medium | Data isolated | Data tier: none |
| Vertically partitioned | Mix | Mix | Mix | Mixed | Per-tier | Per-tier |
Per-model when-to-choose guidance, the sourced Microsoft framing, and each model's test surface are in references/models.md.
Isolation enforcement primitives
Tenant isolation is implemented by combining:
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
tenant_id filter only in application code | One missed query path = cross-tenant leak | Push the filter to the database (RLS) or row-attribute IAM |
tenant_id from request header / body | Spoofable; tenant A can claim to be tenant B | Always derive tenant_id from authenticated JWT/session, never from request payload |
Trust the JWT raw_user_meta_data for tenant claims | User-modifiable per Supabase docs | Use raw_app_meta_data (server-set) or a server-side claim store |
| Single connection pool for all tenants | One slow tenant query blocks all | Per-tenant pools, or quota-aware pools |
| Shared object-storage bucket without prefix isolation | Object enumeration leaks across tenants | Per-tenant prefix + IAM condition on the prefix |
| No isolation tests in CI | Models drift over time | Cross-tenant leak tests in every PR per cross-tenant-data-leak-tests |
| Migration scripts run without tenant context | Schema changes touch all tenants at once; high blast radius | Stamp pattern with progressive rollout |
Test surface
The required test categories per model, plus the per-tier isolation mapping, are in references/test-surfaces.md. The cross-tenant data leak suite is the universal floor: even silo deployments share some surface (account-management APIs, billing, identity providers) where pool-like leaks are possible.
Limitations
References
The four canonical tenant-isolation models
View source (opens in new window)The four canonical tenant-isolation models
Naming across frameworks: Microsoft's automated-single-tenant / fully-multitenant / horizontally-partitioned / vertically-partitioned; AWS Well-Architected's silo / pool / bridge. Deployments are also called stamps or supertenants.
1. Automated single-tenant (silo / fully-isolated)
| Property | Value |
|---|---|
| Compute | Dedicated per tenant |
| Data | Dedicated per tenant |
| Network | Dedicated per tenant |
| Cost per tenant | Highest |
| Blast radius | One tenant |
| Noisy neighbor | None |
Microsoft's framing: deploying a dedicated set of infrastructure per tenant isolates each tenant's data and reduces the risk of accidental leakage.
When to choose: regulated industries with strong isolation mandates (healthcare HIPAA, financial services, government); a small number of high-value enterprise customers; per-tenant configuration is part of the value proposition.
Test surface: deployment automation (the Deployment Stamps pattern); cross-deployment operations like reporting; tenant-to-deployment routing.
2. Fully multitenant (pool / fully-shared)
| Property | Value |
|---|---|
| Compute | Shared |
| Data | Shared (single DB with tenant_id discriminator) |
| Network | Shared |
| Cost per tenant | Lowest |
| Blast radius | All tenants |
| Noisy neighbor | High |
Microsoft's risk framing: separate each tenant's data and don't leak across tenants; a large tenant running a heavy query or operation might affect other tenants.
When to choose: a large number of low-margin customers; high operational efficiency required; tenants accept shared infrastructure.
Test surface: cross-tenant data leak (the canonical risk), tenant_id propagation through every code path, noisy-neighbor behaviour, resource quotas per tenant.
3. Horizontally partitioned (bridge)
| Property | Value |
|---|---|
| Compute | Shared |
| Data | Dedicated per tenant |
| Network | Shared |
| Cost per tenant | Medium |
| Blast radius | App-tier shared, data isolated |
| Noisy neighbor | App-tier yes, data tier no |
Microsoft's framing: a single application tier with an individual database per tenant, which mitigates the noisy-neighbor problem in the data tier.
When to choose: data isolation matters for compliance, but shared compute is acceptable; data-tier noisy neighbors are the dominant failure mode (heavy queries, large indexes).
Test surface: correct database routing per tenant; connection-pool exhaustion under tenant concurrency; cross-DB query attempts must fail.
4. Vertically partitioned
| Property | Value |
|---|---|
| Compute | Mix (some tenants dedicated, others shared) |
| Data | Mix |
| Network | Mix |
| Cost per tenant | Mixed |
| Blast radius | Per-tier decision |
| Noisy neighbor | Per-tier |
Microsoft's framing: a combination of single-tenant and multitenant deployments - most customers' data and application tiers on multitenant infrastructure, with single-tenant infrastructure for customers who require higher performance or data isolation. Includes geographic partitioning (one deployment per region, tenants mapped to the nearest region).
When to choose: the majority of customers fit the shared model, but a minority need silo (enterprise tier); geographic data-residency requirements.
Test surface: every test from the shared model plus every test from the silo model; tenant migration between tiers; pricing tier enforcement.
Source: Microsoft Azure Architecture Center - Tenancy Models learn.microsoft.com/en-us/azure/architecture/guide/multitenant/considerations/tenancy-models (opens in new window).
Test surface by tenant-isolation model
View source (opens in new window)Test surface by tenant-isolation model
Isolation tier mapping
A common pattern is independent isolation per architecture tier:
| Tier | Common choice |
|---|---|
| UI | Shared host name (fully multitenant) |
| API gateway | Shared, with tenant claim in JWT |
| Application services | Shared, tenant context in every request |
| Async queues / topics | Shared topic with tenant_id message attribute, or per-tenant queue |
| Data | Often partitioned: tables with tenant_id (pool); schemas per tenant (bridge); databases per tenant (silo) |
| Object storage | Per-tenant prefix in bucket (pool); bucket per tenant (silo) |
| Search index | Per-tenant routing key (pool); index per tenant (silo) |
The test surface depends on the lowest isolation level in the stack. A fully isolated UI but shared database still requires the full cross-tenant data-leak test battery against the database.
Required test categories per model
| Model | Required test categories |
|---|---|
| Silo / single-tenant | Tenant-to-deployment routing; per-deployment health; deployment automation |
| Pool / fully-shared | Cross-tenant data leak (highest priority); tenant_id propagation; noisy-neighbor mitigation; quota enforcement |
| Bridge / horizontal | Pool tests + database routing per tenant; cross-database query rejection |
| Vertical | Pool + silo tests + tier-migration tests |
The cross-tenant data leak suite is the universal floor: even silo deployments share some surface (account-management APIs, billing, identity providers) where pool-like leaks are possible.
Related skills
cross-tenant-data-leak-tests
Workflow-driven skill that emits the runtime CI gate of cross-tenant leak tests - the actual battery a multi-tenant codebase must pass on every PR. Defines the canonical test patterns (read-other-tenant-by-id, list-leak, spoofed-tenant-id-in-body, JWT-replay, FK-cross-tenant, unique-collision side channel, object-storage IDOR, search-index-direct-query, async-job-context-reload, cache-key-collision), the expected response codes per pattern (404 vs 403 disclosure trade-off), the Postgres-RLS-direct test patterns, and the CI integration (run with non-superuser non-BYPASSRLS role, fail the build on any leak). Use when implementing the actual leak-test suite (after tenant-leak-test-author produces the plan), when adding the CI gate to an existing project, or when investigating a leak finding.
multi-engine-row-level-security-reference
Pure-reference catalog of row/tenant isolation mechanisms across four database engines: MySQL and MariaDB (no native RLS - views with SQL SECURITY INVOKER plus app-layer enforcement), CockroachDB (native RLS via ALTER TABLE ENABLE ROW LEVEL SECURITY and CREATE POLICY, matching Postgres semantics), Vitess (keyspace sharding + vindexes route tenant writes to dedicated shards without a policy layer), and SQL Server (CREATE SECURITY POLICY with inline table-valued function filter/block predicates). Covers the isolation mechanism, tenant-context pattern, bypass risks, and test patterns for each engine. Use when designing or auditing tenant isolation on MySQL, MariaDB, CockroachDB, Vitess, or SQL Server.
row-level-security-postgres-reference
Pure-reference catalog of Postgres Row-Level Security (RLS) for tenant isolation. Covers enabling RLS (ALTER TABLE ... ENABLE ROW LEVEL SECURITY, default-deny semantics), CREATE POLICY syntax (USING vs WITH CHECK clauses, FOR SELECT/INSERT/UPDATE/DELETE/ALL, permissive vs restrictive, TO role_name), bypassing RLS (superuser / BYPASSRLS / table owner / FORCE ROW LEVEL SECURITY), tenant context patterns (current_user, current_setting, JWT claims via Supabase auth.uid() / auth.jwt()), performance discipline (wrapping auth functions in SELECT, index on policy-referenced columns), and anti-patterns. Use as the RLS-pattern reference for Postgres-backed tenant isolation. Consumed by tenant-leak-test-author, cross-tenant-data-leak-tests.
tenant-leak-test-author
Workflow-driven skill that builds a tenant-leak test plan from an inventory of tenant-bearing surfaces (database tables, APIs, object storage, search indices, async messages) and the isolation model in use. Walks through identifying tenant-bearing surfaces, enumerating the attack patterns per OWASP WSTG-ATHZ-02 (horizontal escalation, vertical escalation, IDOR / BOLA), generating test cases that exercise each pattern against each surface, and emitting the test suite skeleton (pytest / Jest / JUnit / Go test) with explicit cross-tenant probes. Use when designing a multi-tenant test suite for a new feature, when auditing test coverage for an existing tenant boundary, or when reviewing PRs that add tenant-bearing surfaces. Distinct from cross-tenant-data-leak-tests which is the runtime gate; this skill produces the plan.
tenant-onboarding-test-author
Workflow-driven skill that authors a test suite for tenant provisioning and offboarding: account creation, isolation at creation (no cross-tenant bleed from a new tenant's first API call), default resource quotas, billing record linkage, seed and default data correctness, idempotent re-provisioning, and teardown with full data deletion. Walks through mapping provisioning surfaces, generating test cases per surface, emitting the test suite skeleton (pytest / Jest / JUnit / Go test), and producing a coverage matrix. Use when a new tenant onboarding flow is introduced or changed, when the offboarding pipeline is modified, or when auditing provisioning coverage before a compliance review. Distinct from tenant-leak-test-author (runtime cross-tenant access) and cross-tenant-data-leak-tests (CI gate): this skill covers the provisioning lifecycle, not steady-state access control.