Private to the owner
Every person sees and changes only their own records. Table:owner_field: "owner_id". Rules:
Client:
from("notes").create({ text }), from("notes"). The owner is filled in for you.
Test: Bob lists, gets, updates and deletes Ada’s note: empty list, then NotFoundError.
Authored content, public to read
A blog where anyone reads and only the author writes.
Set
owner_field: "author_id" so it fills on create.
Authors and editors
Authors edit their own; editors edit anything.- Any of these rules pass:
- Owns the record (field)
author_id - Has role
editor
- Owns the record (field)
update and delete. Note that on list this any is per-row, so totals are null. If lists are large, give editors a separate route or table for review.
Shared with specific people
A document shared with a list of user ids (shared_with, a list column):
- Any of these rules pass:
- Owns the record (field)
owner_id - Compare values:
$record.shared_withcontains$auth.user_id
- Owns the record (field)
list and get. Make update stricter: owner only, or those with "editor" in a separate editors list.
For larger sharing models (per-person permissions, groups), use a shares table (document_id, user_id, role) and check membership in a code rule; JSON can’t query another table.
Can’t forge the owner
Ensure nobody creates a record owned by someone else, even if they send anowner_id:
- All of these rules pass:
- Is signed in: yes
- Compare values:
$input.owner_idequals$auth.user_id
create rule. Because the owner field is filled automatically, this is a belt-and-braces check; you need it if your clients ever send owner_id themselves.
Prevent changing the owner
Only a service may reassign:- All of these rules pass:
- Owns the record (field)
owner_id - Check a value:
$input.owner_idis empty
- Owns the record (field)
update: the owner may edit, but not send an owner_id at all. Mark the owner column read only for the same effect, enforced by the column.
Transfer ownership
Never let the client setowner_id directly. Expose a route POST /documents/{id}/transfer that checks the caller owns it and the target exists, then updates as a service.
Records created by the server for a person
When your server (secret key) creates a record on someone’s behalf, set the owner explicitly, or theowner rule never matches and the person can’t see it:
A person’s profile
One row per user whose id equals the user’s id:- Compare values:
$record.idequals$auth.user_id
id set to the user’s id from a sign-up flow (user.created).
Roles
Roles and permissions.
Teams
Organizations.