padosoft-tenant-isolationlisted
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