Isolation proof
Trace tenant context through every execution path
The strongest design makes missing tenant context fail closed instead of relying on every future query author to remember a filter.
Membership
Resolve who belongs to which tenant and with what role.
- Boundary
- User, tenant, role and invitation lifecycle
- Failure
- Client chooses tenant ID directly
- Proof
- Authoritative membership lookup
Data access
Apply tenant scope to every read and write.
- Boundary
- Rows, relationships and aggregate queries
- Failure
- One report query omits tenant filter
- Proof
- Cross-tenant negative matrix
Async and cache
Carry tenant identity through jobs and reused results.
- Boundary
- Queue payload, scheduler, cache and search index
- Failure
- Cache key omits tenant
- Proof
- Alternating-tenant and replay tests
Files and exports
Keep object paths, signed access and generated files isolated.
- Boundary
- Upload, download, export and retention
- Failure
- Guessable object path bypasses app query
- Proof
- Storage policy and expired-link tests
Support and admin
Constrain exceptional access used to operate the service.
- Boundary
- Impersonation, support query and break-glass
- Failure
- Shared administrator can browse all tenants silently
- Proof
- Approval, purpose, time bound and audit
Operating principle
Negative tests are the evidence of isolation
A successful tenant journey demonstrates functionality. Isolation is demonstrated when the same authenticated user cannot substitute another tenant, object, export, cache key or job reference.
Create two controlled tenants with similar data. Run the same matrix across interface, API, background work, files and administrative paths, recording both denial and absence of side effects.
- Derive tenant context server-side
- Fail closed when context is missing
- Scope caches and jobs
- Audit exceptional access
Applied example
Failure example: cross-tenant cached dashboard
A dashboard cache uses the route name and date range but not the tenant ID. The second company to request the same range receives the first company’s aggregate.
- Database queries were correctly scoped
- The derived cache created a new boundary
- No authorization check occurred on cached content
- Alternating controlled tenants reproduces the leak
Plain answers
Questions to resolve before shipping
Is row-level security enough?+
It can be a strong layer when correctly designed, but files, caches, jobs, external services and administrative access still need isolation.
Can users belong to several tenants?+
Yes, but switching must establish a clear active context and every operation must authorize that membership and role.
Should tenant IDs appear in URLs?+
They can aid routing, but treat them as untrusted selectors. Authorization must derive from trusted membership, not URL possession.