Each recipe shows the rule, the table setup, what the client does, and what to test.

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
Use for 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_with contains $auth.user_id
Use for 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 an owner_id:
  • All of these rules pass:
    • Is signed in: yes
    • Compare values: $input.owner_id equals $auth.user_id
as the 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_id is empty
as 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 set owner_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 the owner 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.id equals $auth.user_id
Create it with id set to the user’s id from a sign-up flow (user.created).

Roles

Roles and permissions.

Teams

Organizations.