Remote / foldkit-remote-drizzle

Which rows a principal may see

authorize decides which fields a principal may read. Which rows exist for them is the binding's visible: a condition over the table's columns, or undefined for every row.

ts
const Db = bind(Blog, {
  Post: {
    table: posts,
    // A visitor sees what is published; an author sees every row.
    visible: principal => (isAuthor(principal) ? undefined : isNotNull(posts.publishedAt)),
  },
})

A rule may also answer with an Effect of the condition, for one that must ask something first (a guard's server-side body), and it is given its own binding as a second argument, (principal, self), so a rule written before bind can name the table's columns through self.columns. foldkit-guard builds its rules that way.

It is on the binding, not on a source, because a table is read four ways and a rule on one would leave three open. Every one applies it:

The table is readWhat a hidden row is
by idnot returned, so the client knows it as NotFound
as the children of a relationnot listed, not counted by a derived count, and not a gap in a page
as the target of a one refthe ref reads null, so it does not say the row exists
through a querynot in any page; an id cursor on a hidden row does not resolve
  • A required one whose target can be hidden will read null for a principal who may not see it, which its schema refuses. Make such a relation { optional: true }, or make the owner's visible depend on the target's.
  • A relation's own policies still apply, beside the target's visible.
  • entity(name, table, { visible }) takes the same rule.