skills/multi-tenant-architecture-expert/SKILL.md
Tenant isolation, row-level security, shared/siloed schema patterns for SaaS platforms. Activate on: multi-tenant, tenant isolation, RLS, shared database, SaaS architecture, tenant context, data isolation. NOT for: connection pooling (use database-connection-pool-manager), API gateway routing (use api-gateway-reverse-proxy-expert).
npx skillsauth add curiositech/windags-skills multi-tenant-architecture-expertInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Design and implement multi-tenant SaaS systems with proper data isolation, tenant context propagation, and noisy neighbor prevention.
Activate on: "multi-tenant", "tenant isolation", "RLS", "shared database", "SaaS architecture", "tenant context", "data isolation", "noisy neighbor", "tenant onboarding"
NOT for: Connection pool sizing → database-connection-pool-manager | API gateway tenant routing → api-gateway-reverse-proxy-expert | Authorization framework → relevant auth skill
| Domain | Technologies | |--------|-------------| | Database RLS | PostgreSQL RLS, Supabase RLS, Neon branch-per-tenant | | Schema Isolation | PostgreSQL schemas, schema_search_path | | Tenant Context | AsyncLocalStorage (Node), cls-hooked, middleware injection | | ORMs | Prisma multi-schema, Drizzle with RLS, TypeORM tenant scope | | Infrastructure | Kubernetes namespaces, AWS Organizations, tenant-aware CDN |
-- Enable RLS on all tenant tables
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Policy: tenants see only their own data
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.current_tenant')::uuid);
-- Set tenant context per request (from middleware)
SET LOCAL app.current_tenant = 'tenant-uuid-here';
-- All queries now automatically filtered
SELECT * FROM orders; -- only returns current tenant's orders
import { AsyncLocalStorage } from 'node:async_hooks';
interface TenantContext { tenantId: string; plan: 'free' | 'pro' | 'enterprise'; }
const tenantStore = new AsyncLocalStorage<TenantContext>();
// Middleware: extract tenant from JWT and propagate
function tenantMiddleware(req: Request, res: Response, next: NextFunction) {
const tenantId = req.auth?.tenantId; // from JWT
if (!tenantId) return res.status(403).json({ error: 'Tenant required' });
const ctx: TenantContext = { tenantId, plan: req.auth.plan };
tenantStore.run(ctx, () => {
// Set PostgreSQL session variable for RLS
req.db.query(`SET LOCAL app.current_tenant = $1`, [tenantId]);
next();
});
}
// Anywhere in the stack:
export function getCurrentTenant(): TenantContext {
const ctx = tenantStore.getStore();
if (!ctx) throw new Error('No tenant context — called outside request?');
return ctx;
}
Shared DB + RLS Schema/Tenant DB/Tenant
Cost per tenant Lowest Medium Highest
Data isolation Row-level Schema-level Full
Compliance Moderate Good Best
Migration effort Single migration Per-schema Per-database
Max tenants 10,000+ 1,000 100
Cross-tenant query Easy Possible Hard
Noisy neighbor Risk (mitigate) Moderate None
/api/tenant-123/orders lets users guess other tenant IDs; use JWT claims insteadcache:orders:123 is wrong, cache:tenant-abc:orders:123 is rightdata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.