module-18/tenant-fence.mdDELIVERY STANDARD

The Tenant Fence

Cross-tenant access isn't carefully avoided. It's structurally impossible.

A missed WHERE clause in one query showed Customer A three rows of Customer B's data — the deal-killing bug every multi-tenant system is one code review away from.
Tenant isolation enforced below the application: row-level security or schema isolation as the floor, tenant context injected at the connection layer, cross-tenant access structurally impossible rather than diligently avoided.

The incident

One query, deep in an export feature, was missing its tenant filter. It had been missing it for months. Customer A downloaded a CSV with three rows of Customer B's data in it — and Customer A was security-conscious enough to notice, report it, and ask the question every B2B vendor dreads: 'how do we know this is the only one?' There was no good answer, because the isolation model was 'every developer remembers the WHERE clause, every time.'

Why the obvious approach fails

Filtering by tenant_id in application queries makes isolation a per-query act of discipline — and discipline fails statistically. Every new query, every ORM shortcut, every hand-written report is another chance to forget, and one miss is a breach. The question a serious buyer asks isn't 'do you filter?' but 'what happens when a developer forgets?' The only good answer moves enforcement below the application: row-level security or schema isolation at the database layer, with tenant context bound to the connection — so a query without a tenant scope doesn't leak, it returns nothing.

The standard

  • Isolation is enforced at the database layer — row-level security policies or per-tenant schemas as the floor; app-level WHERE clauses are a convenience, never the guarantee.
  • Tenant context binds at connection/session level from the authenticated identity; queries cannot opt out of it.
  • The isolation tier (shared-RLS / isolated schema / isolated database) is a declared architectural decision per deployment, with the trade-offs documented.
  • Cross-tenant operations (support tooling, billing rollups) go through an explicit, audited, break-glass path — never through disabled policies.
  • Isolation is continuously tested: a permutation suite attempts cross-tenant reads on every entity, on every release.

How Cortex validates it

At plan time

multi-tenant blueprints declare the isolation tier and the RLS/schema policy set before any table is created.

At validation

assert: query_without_tenant_scope → zero_rows (not other_tenant_rows)assert: cross_tenant_suite.leaks == 0 across all entitiesassert: break_glass_access.audited == 100%

Assertion syntax is illustrative.

At review

the approver sees the hostile suite: every entity probed across tenant boundaries, zero leaks, and the one legitimate break-glass path fully logged.

Composes with

See where your codebase stands against this standard.