Guard / foldkit-guard
Deciding reads and writes
A server asks the Policy which rows a principal may read. Every principal
guard is decided by its body (once, however many rules name it); what is
left is a Condition over the row, which an interpreter runs:
ts
import { Condition } from 'foldkit-guard'
const ada = Option.some({ id: 'ada', role: 'member' as const, projectIds: ['p1'] })
const readable = Effect.runSync(Policy.readable(Todo, ada))
// isAdmin was false, so: isOwner or inProject, over each row
const t1 = { id: 't1', ownerId: 'ada', projectId: 'p9', title: 'Write the README' }
Condition.holds(readable, t1, Policy.inputOf(ada), () => false)
// trueCondition.alwaysandCondition.neverare a read settled without looking at rows: an admin, or a visitor whose rules all read the principal.Condition.holdsis how an interpreter that has the rows runs it; a database compiles it instead. Its last argument answers an inherited Read, which only the caller can look up.Policy.readMembers(Todo, principal, members)decides each member's own Read rule, with the refusal it shows (OmitorRedact).- Writes are decided alike, each principal guard's body told the interaction:
Policy.writable(Todo, principal, 'Update')is the row's rule for it,{ condition, deny }(Forbidden, orExplainwith its sentence);Policy.writeMembers(Todo, principal, members, 'Update')each member's;Policy.linkable(Todo, principal, 'project', 'Link')a relation's, when it has one.