Kizaki
ReferenceInspire

Policies

Use grant and deny rules to control who can read, write, update, and delete entity rows.

Policies are the core of Kizaki’s data access model. They live on entities and decide who can read, insert, update, or delete rows. The runtime enforces them everywhere that data can be accessed, which is why they are one of the most important parts of the schema.

The Evaluation Model

The public model is:

  1. Kizaki looks up the policies for the entity and action
  2. it finds the grants that match the current principal
  3. it unions those matching grants together
  4. it applies deny rules after grants
  5. if nothing grants access, the operation is denied

A few rules fall out of that model:

  • no grants means no access
  • deny always wins
  • the same policy model applies across reads, writes, includes, routes, and live queries

Grant Syntax

entity Document {
  title: string,
  ownerId: __User.id,

  @grant read, write where resource.ownerId == principal.id
  @grant read to role(Admin)
}

Use grants to express the positive rule: who should be allowed to act and under what condition.

What Actions Mean

  • read controls visibility
  • write is shorthand for insert and update behavior
  • delete controls row deletion

Start simple. In most products, read, write, and delete are the only policy actions you need to think about at first.

Principals

Kizaki policies are usually written against one of three principal shapes:

  • * for any authenticated user
  • @public for unauthenticated access
  • role(Name) for a specific role in the principal context

That gives you the core building blocks for personal apps, team apps, public content, and admin surfaces.

Deny Syntax

entity AuditLog {
  action: string,

  @grant read to role(Admin)
  @deny write to *
}

Use deny rules for invariants that must always hold. A deny is not your main modeling tool. It is the backstop for actions that should never happen through the normal application path.

Ownership, Roles, And Public Access

These are the three patterns you will use most often.

Ownership

entity Project {
  name: string,
  ownerId: __User.id,

  @grant read, write, delete where resource.ownerId == principal.id
}

Use ownership when the data belongs to one user and the rule is naturally “you can act on your own rows.”

Roles

entity Invoice {
  total: decimal(10, 2),
  accountId: Account.id,

  @grant read to role(Admin)
  @grant read where resource.accountId == principal.accountId
}

Use roles when access is organizational rather than personal.

Public Reads

entity PublicPost {
  title: string,
  published: boolean = false,

  @grant read to @public where resource.published == true
}

Use @public only when anonymous access is genuinely part of the product.

Field-Level Grants

Policies do not only control which rows are visible. They can also narrow which fields may be returned for a given principal.

entity PatientRecord {
  patientId: __User.id,
  diagnosis: string,
  billingCode: string,

  @grant read(patientId, billingCode) to role(Billing)
  @grant read to role(Doctor)
}

This is useful when different roles should see different slices of the same row. It also pairs well with .fields() in server and browser reads so the app only returns what the screen actually needs.

Via Grants

Use via when access should flow through a related entity rather than a direct field on the resource.

entity Doc {
  title: string,

  @grant read to * via DocShare
}

entity DocShare {
  docId: Doc.id,
  userId: __User.id,

  @unique([docId, userId])
}

This is the right pattern for sharing, memberships, invitations, and other relationship-based access models.

  • start with ownership rules
  • add role-based grants for admin and team flows
  • use denies for invariants that must always win
  • keep policies on the entity that owns the data
  • prefer simple, readable conditions over clever policy expressions

Practical Design Guidance

For most products, the clean progression is:

  1. ownership first
  2. role-based reads next
  3. role-based writes when needed
  4. field-level restrictions for sensitive surfaces
  5. via rules for sharing and membership models

If you follow that order, the schema usually stays understandable even as the app grows.

What Policies Do Not Replace

Policies decide row- and field-level data access. They do not replace:

  • function-level admission checks for “who can invoke this workflow”
  • UI decisions about what to render
  • business logic inside your exposed functions

The clean split is:

  • policies own data access
  • functions and routes own workflows
  • frontend code owns presentation

Related guide: Authorization

On this page