/ 01The RLS model
Row-Level Security in Fieldbook is policy-based. A table has zero or more RowPolicy entries. When a user reads, writes, or deletes from the table, the active policies are evaluated against each row and the principal; only rows that pass are visible.
Three properties to remember:
- Owner bypasses all policies — the Owner role can always see and edit everything (and every Owner read is audited).
- OR across policies — if any enabled policy grants access, the row is visible.
- Fail-closed — if a policy errors during evaluation, the row is excluded, not included.
/ 02Row-policy schema
pub struct RowPolicy { pub id: String, pub name: String, pub operation: Option<RowPolicyOperation>, // Select | Insert | Update | Delete | All pub applies_to_roles: Vec<Role>, // empty = all roles pub rules: Vec<RowPredicate>, // AND-ed together pub enabled: bool, } pub struct RowPredicate { pub field_id: String, pub operator: PredicateOp, pub value: String, // may reference $principal.email, $role, ... }
/ 03Predicate operators
| Operator | Semantics | Example |
|---|---|---|
Eq | Equals (case-sensitive) | team = "alpha" |
Neq | Not equal | status != "archived" |
Contains | Substring match | tags ~ "vip" |
In | Membership in comma-list | region in "eu,uk" |
StartsWith | Prefix match | account_id starts "ENT-" |
IsEmpty | Null or empty string | archived_at IsEmpty |
IsNotEmpty | Has a value | assignee IsNotEmpty |
/ 04Variables
Policy values can reference the active principal — the authenticated user opening the file or executing the request. Variables are resolved at evaluation time, never inlined into stored policies.
| Variable | Resolves to |
|---|---|
$principal.email | Authenticated email address |
$principal.sub | OIDC subject identifier |
$principal.name | Display name |
$role | The role's name string |
If a policy uses a variable the session cannot resolve (anonymous session, expired principal), the predicate evaluates to false and the row is excluded.
/ 05Evaluation logic
Owner shortcut
If the principal's role is
Owner, the row is visible. Audit-only.Collect enabled policies
Filter
row_policiesto those that are enabled and whoseoperationcovers the current operation.Filter to applicable roles
If
applies_to_rolesis non-empty, drop policies that do not mention the principal's role.If no policies remain
The row is visible. Tables without policies behave like ordinary SQL tables.
Evaluate each remaining policy
All predicates in a policy must pass (AND). If any policy passes, the row is visible (OR across policies). On evaluation error, the policy is treated as not passing.
Audit
Aggregate counts of granted vs denied rows recorded in a single
RlsFilterAppliedentry per request, not per row.
/ 06The visual editor
The RLS editor (desktop and macOS) lets non-engineers compose policies. Three panes:
- Policy list — every policy on the table, with enable/disable toggle, operation, and role filter
- Rule builder — predicate-by-predicate construction; field picker, operator picker, value with variable autocomplete
- Test panel — pick a principal, see which rows would be visible; row counts annotated by which policy granted them
Every save emits RlsPolicyAdded, RlsPolicyModified, or RlsPolicyRemoved.
/ 07Testing policies
Two ways: the test panel (above), or the CLI:
fieldbook rls test FILE \ --table tbl_clients \ --as user@firm.com \ --role Viewer \ --operation Select
The CLI runs the policies against every row in the table and prints a per-row breakdown — visible, denied, by which policy, with the predicate trace for failed ones. Use this in CI when generating wall policies (see Chinese Wall) to assert isolation before deploy.
