Free SKILL.md scraped from GitHub. Clone the repo or copy the file directly into your Claude Code skills directory.
npx versuz@latest install event4u-app-agent-config-agent-src-uncompressed-skills-multi-tenancygit clone https://github.com/event4u-app/agent-config.gitcp agent-config/SKILL.MD ~/.claude/skills/event4u-app-agent-config-agent-src-uncompressed-skills-multi-tenancy/SKILL.md---
name: multi-tenancy
description: "Use when working with the multi-tenant architecture — customer DB switching, FQDN routing, tenant isolation, or cross-tenant operations."
source: package
domain: engineering
---
# multi-tenancy
## When to use
Use this skill when working with tenant-specific data, customer database connections, or any code that touches the dual-database architecture.
Do NOT use when:
- Single-database applications
- Frontend-only changes
## Procedure: Work with multi-tenancy
1. **Gather context** — read `agents/docs/` for multi-tenant architecture, `config/database.php` for connection definitions, and search for the tenant switching service.
2. **Identify connection** — determine whether the code touches central, tenant, or both databases. Set `$connection` explicitly on any new model.
3. **Implement** — write the feature using the correct connection. Use the tenant switching service for cross-tenant operations. Never mix connections in a single query.
4. **Verify isolation** — inspect the code for tenant leaks: global scopes, missing `$connection`, shared caches, or job serialization without tenant context.
5. **Test** — write a test that exercises the tenant boundary: seed tenant-specific data, switch context, verify correct data is returned and other tenants' data is invisible.
## Architecture overview
```
Request → Identify Tenant (JWT / subdomain / API key)
→ Lookup credentials from central database
→ Reconfigure tenant connection at runtime
→ All tenant queries use tenant connection
```
### Dual-database pattern
| Connection type | Purpose | Scope |
|---|---|---|
| Central / shared | Global data — tenants, config, shared resources | Shared across all tenants |
| Tenant / customer | Tenant-specific data — domain entities | One per tenant |
The central connection is typically the **default connection**.
The tenant connection starts **empty** and is configured dynamically per request.
### Additional connections
Projects may have additional connections for admin operations, provisioning, or monitoring. Check `config/database.php`.
## Core tenant switching service
Search the codebase for the service responsible for tenant switching. Typical responsibilities:
1. Store tenant context (e.g., in Laravel Context or a singleton)
2. Load tenant configuration
3. Set monitoring context (tenant ID, name, domain)
4. Reconfigure the database connection with tenant credentials
5. Bind tenant-specific services via the container
## Model conventions
### Setting `$connection`
Every model **must** explicitly set its connection:
```php
// Central models
class Tenant extends Model
{
protected $connection = 'central_database';
}
// Tenant models
class Project extends Model
{
protected $connection = 'tenant_database';
}
```
Check the project for the actual connection names and namespace conventions.
## Tenant isolation rules
- **Never query the tenant connection before the tenant is set.**
- **Never mix connections in a single query** — use explicit joins or separate queries.
- **Always specify `$connection`** on new models — never rely on the default.
- **Use transactions per connection** — `DB::connection('tenant_database')->transaction(...)`.
- **Artisan commands** that need tenant access must use the appropriate trait (search for it in the project).
## Testing with tenants
- Tests use dedicated tenant seeders (check `agents/docs/` for seeder conventions).
- The testing database may consolidate multiple connections into a single DB for simplicity.
- Use `RefreshDatabase` or manual seeding — never assume a specific tenant state from previous tests.
## Common pitfalls
| Problem | Solution |
|---|---|
| Query on `customer_database` returns empty | Customer not set yet — check middleware order |
| Race condition in parallel tests | Each test process gets its own DB (parallel testing with separate DBs) |
| Wrong tenant data in background jobs | Serialize customer ID, re-resolve in job's `handle()` method |
| Migration on wrong connection | Specify `--database=customer_database` or set `$connection` in migration |
## Output format
1. Tenant-aware code with correct DB connection switching
2. Verification that tenant isolation is maintained
## Auto-trigger keywords
- multi-tenant
- tenant isolation
- customer database
- FQDN routing
## Gotcha
- Always verify which database connection is active before running queries — cross-tenant data leaks are critical bugs.
- The model forgets to switch back to the main connection after tenant operations.
- Queue jobs serialize the connection state — ensure the tenant context is restored when the job runs.
- Don't use `DB::connection()` directly — use the tenant switching helpers.
## Do NOT
- Do NOT hardcode database names — always use connection names.
- Do NOT assume `customer_database` is available in service providers or early boot.
- Do NOT access tenant data in global middleware that runs before customer identification.
- Do NOT store tenant DB credentials in code — they come from the `customer_databases` table.