For B2B apps, data belongs to an organization. People belong to several; each session acts in one (Organizations). The session carries org and org_role.

Setup

  1. Add an org column (text, indexed) to every team table.
  2. Set it on create from the session: org: session.org.
  3. Apply a team rule to every action.

Members see their organization’s records

  • All of these rules pass:
    • Check a value: $auth.org is set
    • Compare values: $record.org equals $auth.org
The truthy line is essential. If the caller has no active organization and a record has none either, eq of two nulls is true, and everyone sees everything. Always require the caller’s side. Save it as a named rule team_member, and use it on list, get, update and delete.

Create inside your organization

  • All of these rules pass:
    • Check a value: $auth.org is set
    • Compare values: $input.org equals $auth.org
so nobody can create a record in a different organization by sending a different org.

By organization role

Members read; admins and owners change and delete: (Use named rules for update and delete too: team_admin, team_owner.)

Personal and team records on one table

A record is either personal (owner_id) or team (org):
  • Any of these rules pass:
    • Owns the record (field) owner_id
    • All of these rules pass:
      • Check a value: $auth.org is set
      • Compare values: $record.org equals $auth.org

Platform staff across organizations

Your own support team needs to see all organizations: Use a Custom condition with Any of: Has role platform_support, or your named rule team_member. Prefer a separate staff tool with a secret key behind your own login, rather than a global role in customer-facing rules.

Files and channels per organization

  • Storage: key <org>/…, and object.segments.0equalsobject.segments.0 equals auth.org (Storage).
  • Channels: name org:<slug>:… and require truthy $auth.org (Realtime).

Switching organizations

switchOrg(slug) refreshes the token with the new org. All the rules above follow. Don’t cache lists across a switch: reload on TOKEN_REFRESHED.

Invitations and access

New members get access the moment they accept an invitation and switch to the organization (Invitations). Removing a member removes their role; their existing sessions lose the organization at the next refresh.

Test the boundary

The last assertion is the one people forget.

Organizations

Create and switch.

Values and paths

The null trap.