Kizaki
Guides

Authorization

Model row access with ownership, roles, and deny rules instead of spreading checks through handlers and UI code.

Start with the simplest rule that matches your app:

  • ownership for personal data
  • roles for admin access
  • denies for hard stops such as immutable records
entity Document {
  title: string,
  ownerId: __User.id,

  @grant read, write where resource.ownerId == principal.id
  @grant read to role(Admin)
  @deny delete to *
}
  • keep policies on the entity that owns the data
  • prefer declarative policy conditions over hand-written permission checks
  • use .fields() in queries to minimize what leaves the server

A Good Mental Model

Think about authorization in layers:

  1. who can invoke a workflow belongs at the exposed-function or route boundary
  2. which rows are visible or mutable belongs on the entity
  3. which fields should come back is often a combination of field-level grants and .fields()

This split prevents a common failure mode in other stacks, where an endpoint is protected but still reads or returns more data than intended.

Ownership First, Then Roles

For most products, the cleanest path is:

  • start with ownership-based rules
  • add role-based rules only when the product model truly needs them
  • use deny rules for invariants, not as your primary policy style

That keeps policies readable, especially when AI tools or multiple engineers are working in the same schema.

Why This Matters For The Rest Of The Platform

Kizaki uses the same authorization model across:

  • normal reads
  • updates and deletes
  • includes
  • browser live queries
  • routes and API-key-backed access

That consistency is the real reason to invest in good policies early.

On this page