Tenant isolation is a system property
Multi-tenancy is not complete when a tenant ID is added to a few tables. Isolation must survive every query path, background task, file lookup, export, cache key, webhook, and administrative action.
The safest design makes the secure path the default path. Relying on every developer to remember a filter in every query creates an avoidable class of data exposure.
Choose an isolation model deliberately
Shared-schema tenancy is operationally efficient and works well for many products when enforcement is centralised and tested against a real database. Separate schemas or databases can offer stronger boundaries at the cost of greater operational complexity.
- Define tenant ownership for every tenant-scoped record
- Enforce scope below the controller or UI layer
- Test cross-tenant access using production-like persistence
- Include jobs, exports, search, cache, and storage in threat modelling
Permissions and audit history answer different questions
Permissions define what an actor may do now. Audit history explains what happened before. Sensitive workflows often need both.
Append-only audit records should capture the actor, tenant, action, target, time, and relevant context without becoming a second uncontrolled copy of sensitive data.
Files and asynchronous work expand the boundary
File extensions and browser-provided MIME types are not adequate validation. Inspect content signatures, constrain size and type, isolate processing, and control download authorisation.
Background jobs must carry explicit tenant context and remain idempotent. A retry should not charge a customer twice, send duplicate notifications, or process data for the wrong workspace.