Defence-in-depth multi-tenancy for Laravel applications that share one database and one schema.
In a shared-schema SaaS, every tenant's rows live in the same tables, separated only by a
tenant_id column. It is the cheapest tenancy model to run and the easiest one to leak. One
forgotten where() is a cross-tenant data breach.
Tenant Guard's answer is not a single clever scope — it is five independent layers, each of which fails closed on its own:
- Query Scope — every Eloquent read is constrained automatically.
- Write Guard — cross-tenant creates, updates and deletes are refused, and the tenant key is immutable.
- SQL Sentinel — catches raw SQL and
DB::table()calls before they execute. - Propagation — the tenant follows the request into queued jobs and, optionally, the cache.
- Static Audit — a command that flags models and tables missing tenant protection.
It sits on top of connection switchers like stancl/tenancy and spatie/laravel-multitenancy
rather than replacing them — see the interoperability section of the
usage guide.
composer require ifds-oss/tenant-guard
php artisan tenant-guard:install
php artisan migrateuse Ifds\TenantGuard\Concerns\BelongsToTenant;
class Post extends Model
{
use BelongsToTenant;
}Route::middleware('tenant')->group(function () {
Route::get('/posts', [PostController::class, 'index']);
});That is the whole integration. The controller needs no where() clause — Post::all() only ever
returns the current tenant's rows.
The full guide — every layer, the resolver chain, the API, events, testing helpers, gotchas and architecture — lives in USAGE.md.
See CHANGELOG.md for release notes.
MIT. See LICENSE.