← ClaudeAtlas

padosoft-tenant-isolationlisted

Use this skill when more than one organisation, customer, workspace or project shares a system, and whenever a boundary between them is being written or reviewed: a scoped query, a list endpoint, an update or delete, a background job, a cache or storage key, an idempotency key, a provisioning or admin route. It also applies when the user reports that one account can see or overwrite another's data, that a count or a page leaks records, that a job ran for the wrong tenant, or that a legacy record is visible to everyone. It checks that the scope is carried by the key and re-checked at every boundary, not applied as a filter on the way out. Do not use it for authentication or login flows, for role and permission design inside a single tenant, or for database sharding and capacity work.
padosoft/skills · ★ 1 · API & Backend · score 80
Install: claude install-skill padosoft/skills
# Tenant isolation Filtering by tenant is what you do on the way **out**. Isolation is what the key carries on the way **in**. Every leak below happened in a system that filtered correctly. **The rule: the scope is part of the identity of the record, not a predicate applied to it.** --- ## 1. A filter protects reads. It does not protect writes ```text ❌ WHERE org = :org → then UPDATE ... WHERE id = :id ✅ the key IS (org, id) → an update names the tenant, or it names nothing ``` Two records with the same name in different tenants are two records. If the storage key does not carry the authenticated scope, a `PUT` or a `DELETE` on a familiar name overwrites the other tenant's copy — and the list endpoint that "proved" isolation was never involved. This holds for every store, not just the database: an object-storage prefix, a cache key, a search document id, a file path. **Derive the prefix from the authenticated scope**, never from something the caller sent. ## 2. Filter first, then paginate ```text ❌ LIMIT 20 ... then filter by tenant → the page is short, or empty, and the count is wrong ✅ tenant + predicate → count → slice ``` Computing a total before the scope is applied leaks **counts and identities** through page boundaries: the caller learns how many records exist that they cannot see. The same ordering bug appears without tenancy — applying a limit before a selective predicate is a correctness defect, not a performance one, because a newer ro