Connecting
Learn/L15 · Your stack (VPS repos)

Tenant isolation (why rows have tenantId / organizationId)

Every customer sees only their rows because queries filter by tenant or organization.

Difficulty 3/5

Plain English

Agentify uses tenantId + getTenantUser. Jmcon uses organizationId + getActor. If a query forgets the filter, that's a data leak — the code review pattern is always 'resolve actor, then withIndex'.

Analogy

Apartment building: same hallway, locked doors per unit.

Jessica's wedding portal

getActor(ctx) returns role + organizationId; client logins only see one eventId.

Agentify CRM

Pattern: getTenantUser(ctx, tenantId) at top of mutation; .withIndex('by_tenant_*', q => q.eq('tenantId', tenantId)).

Go deeper

One business = one tenant/org id on each row.

Common mistakes

  • Trusting client-sent tenantId without server check.
  • Copy-paste query from admin tool into user-facing code.

AI-agent trap

Broad queries without tenant filter — treat as Sev-1 until proven safe.

Summary

Never use .filter on tenantId in production hot paths — use indexes.

Confidence: