feather check fails on a tenant-scoped model queried without a tenant filter.
You’ll use: TenantScopedMixin, get_current_tenant_id(), require_same_tenant(),
domain-based tenant assignment, platform admin, and a separate Attachment model.
Create the project
Multi-tenancy changes the user model and the admin panel, so start fresh again.
There’s no database type prompt this time. Multi-tenant apps require PostgreSQL.
What the scaffold already gives you
Step 1: Tenant-scoped models
The important line isTenantScopedMixin on Kanban. Columns and cards inherit isolation
through their relationship to a board, so they don’t carry a tenant_id of their own.
models/kanban.py
models/column.py
models/card.py
models/attachment.py
models/__init__.py
Account and AccountUser both live in models/account.py, and there is no
models/account_user.py. Keep them along with Log. The scaffolded
services/admin_service.py and seeds.py import all three, and the app won’t start
without them.attachment_path on the card. Here attachments become their own
model, which is what lets a card hold several files.
Step 2: The migration
Step 3: Tenant-scoped services
Two functions carry the isolation.get_current_tenant_id() reads the tenant off the
signed-in user, and require_same_tenant() raises if a resource belongs to anyone else.
services/kanban_service.py
services/column_service.py
services/card_service.py
services/attachment_service.py
services/__init__.py
Steps 4 to 7: Routes, templates and CSS
Delete the scaffolded home page first, since the dashboard replaces it:Step 8: Test the isolation
1
Sign in as the platform admin
You get an empty dashboard. Create a board, open it, add columns and cards. Then visit
Admin, then Tenants.
2
Create a tenant
Name “Acme Corp”, slug “acme”, domain “acme.com”, set to Active.
3
Sign in as an acme.com user in a private window
They land on the pending approval page, because new users need approving.
4
Promote them to tenant admin
As platform admin, go to Admin, Tenants, Acme Corp, Users. Set their role to admin and
activate them.
5
Create boards as that user
They’re isolated from every other tenant.
6
Approve a second acme.com user
Sign in as one in another private window, then approve them from Admin, Users as the
Acme tenant admin rather than as the platform admin.
Platform admins manage tenants, not users. Approving new signups is each tenant admin’s
job, which is what keeps a platform operator out of a customer’s user list.
Prompt Claude
Checkpoint
Sign in as two accounts on different email domains, then confirm:- Each is assigned to the tenant matching its domain
- Neither can see the other’s boards
- Fetching the other tenant’s board by ID returns 403 rather than data
- A card cannot be moved into another tenant’s board
- A tenant admin can manage users inside their tenant only
- The platform admin can see
/admin/tenantsand create tenants feather checkreports notenant-isolationerrors
What you learned
- Isolating data per organization instead of per user
TenantScopedMixinand thefor_tenant()query it addsget_current_tenant_id()at the route layerrequire_same_tenant()as a hard stop in services- Isolation inherited down a hierarchy, from tenant through board to attachment
- Putting the tenant ID in storage paths as well as the database
- Assigning users to tenants by email domain
- Platform admin versus tenant admin, and why approval belongs to the tenant
Next: put it on the internet
A VPS, Docker, automatic TLS, nightly backups and a deploy on every push.