Skip to main content

Table configuration patterns

Owner-based access

{ "ownerColumn": "userId" }
Users access rows where ownerColumn matches their user ID.

Organization-based access

{ "orgColumn": "orgId" }
Users access rows where orgColumn matches any of their organization memberships.

Admin bypass

{
  "ownerColumn": "userId",
  "bypassScopes": { "read": "tasks:viewAll", "write": "tasks:editAll" }
}
Users with the specified scope bypass ownership restrictions.

Group membership access

{
  "ownerColumn": "authorId",
  "groupAccess": [{
    "column": "teamId",
    "membershipTable": "TeamMember",
    "membershipColumn": "teamId",
    "scopes": { "read": "tasks:viewTeam", "write": "tasks:editTeam" }
  }]
}
Users with the required scope and group membership can access matching rows.

Scope-gated access

{ "requiredScopes": { "read": "settings:view", "write": "settings:edit" } }
No owner — access is purely scope-based.

Inherited access

{
  "accessVia": {
    "column": "taskId",
    "parentTable": "Task",
    "parentColumn": "id"
  }
}
Child entity inherits all access rules from the parent.

Access evaluation order

  1. Owner access (ownerColumn match)
  2. Group membership access (scope + membership)
  3. Bypass scopes (scope match)

RLS context

Injected automatically into every ctx.db query:
FieldDescription
userIdAuthenticated user’s ID
roleUser’s assigned role
userScopesUser’s permission scopes
userOrgIdsOrganization IDs the user belongs to

Pattern selection guide

QuestionPattern
Each row has an owner?ownerColumn
Multi-tenant data?orgColumn
Admins should see everything?Add bypassScopes
Team/group access needed?Add groupAccess
Shared resource, no owner?requiredScopes
Child of another table?accessVia