You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: proper release-note entry for record-scoped allowRead; group the allow* change
The allowRead change was only mentioned inside the Filtered Vector Search
paragraph, where nobody scanning 5.2 for authorization changes would find
it — and the upgrade section referred to 'the read and write hooks above'
when the read hook had no entry above. The read, write, and upgrade notes
now live under one Record-Scoped Authorization section; the vector-search
paragraph links to it instead of carrying the exposition.
Vector searches combined with filters now evaluate the filter during HNSW graph traversal, so the query keeps exploring until it has enough matching nearest neighbors instead of post-filtering a fixed candidate set (which under-filled results under selective filters). Filters can come from query conditions, a JS-API `vectorFilter` function, or a record-scoped `allowRead` override — overriding `allowRead` on a table now makes it a row-level access check, evaluated per record with `this` bound to the record (closing the gap where a collection scan could return rows a single-record GET would deny). With it, a restricted user's vector search returns the k nearest records they are allowed to see. Very selective conditions automatically use an exact scan instead of graph traversal, and a `filterExpansion` visit budget bounds traversal cost. See [Vector Indexing](/reference/v5/database/schema#vector-indexing).
15
+
Vector searches combined with filters now evaluate the filter during HNSW graph traversal, so the query keeps exploring until it has enough matching nearest neighbors instead of post-filtering a fixed candidate set (which under-filled results under selective filters). Filters can come from query conditions, a JS-API `vectorFilter` function, or a record-scoped `allowRead` override (see [Record-Scoped Authorization](#record-scoped-authorization) below) — with it, a restricted user's vector search returns the k nearest records _they are allowed to see_ rather than "nearest k, minus redacted". Very selective conditions automatically use an exact scan instead of graph traversal, and a `filterExpansion` visit budget bounds traversal cost. See [Vector Indexing](/reference/v5/database/schema#vector-indexing).
16
+
17
+
## Record-Scoped Authorization
18
+
19
+
The `allow*` authorization hooks on tables are now **record-scoped when overridden**: instead of a single collection-scope verdict per request, an application-overridden hook is evaluated once per record, with access to that record's fields. Framework defaults are unchanged — a table that does not override a hook keeps its single request-entry RBAC check with no per-record cost — and the change applies only to classes that extend a table; a plain `Resource` subclass defines its own semantics and keeps the single entry check.
20
+
21
+
### Row-Level Read Access Control (`allowRead`)
22
+
23
+
Overriding `allowRead` on a table now makes it a row-level access check. During collection queries — including scans, searches, and vector traversals — it is evaluated once per candidate record with `this` bound to the (frozen) record, so checks like `return this.ownerId === user.id` read naturally; rows it denies are filtered out of the results. This closes the gap where a collection scan could return rows a single-record `GET` would deny. Single-record `get(id)` keeps its request-entry evaluation with the record loaded (a denied record returns a `403`), and subscriptions grant the connection at subscribe time, then filter delivery per event so a subscriber receives only the row changes the check permits — with live subscriptions periodically re-authorized so a revoked grant tears the subscription down.
24
+
25
+
Per-record evaluation requires a synchronous override (query traversal cannot await): an `async allowRead` keeps the single request-entry check and logs a warning. Verdicts are memoized per query, and a thrown exception denies the record (fail closed). See [Record-Level Access Control](/reference/v5/database/schema#record-level-access-control-record-scoped-allowread).
The write-side authorization hooks are now record-scoped when overridden, matching the record-scoped `allowRead` model. A conditional `DELETE` evaluates an overridden `allowDelete` once per matching record with filter semantics — permitted rows are deleted, denied rows are skipped — and an array `PUT` authorizes each element individually (`allowUpdate` for existing records, `allowCreate` for new ones), failing the whole request atomically on any denial. During per-record evaluation `this` is a per-row resource with the record loaded, so row-level checks like `this.ownerId === user.id` and `super.allow*` composition work naturally; `async` overrides are supported on these paths. Non-overridden (default RBAC) hooks keep their single request-entry check with no per-record cost. See [Record-Level Access Control for Writes](/reference/v5/database/schema#record-level-access-control-for-writes-record-scoped-allowdelete--allowupdate--allowcreate).
29
+
The write-side authorization hooks get the same treatment when overridden. A conditional `DELETE` evaluates an overridden `allowDelete` once per matching record with filter semantics — permitted rows are deleted, denied rows are skipped — and an array `PUT` authorizes each element individually (`allowUpdate` for existing records, `allowCreate` for new ones), failing the whole request atomically on any denial. During per-record evaluation `this` is a per-row resource with the record loaded, so row-level checks like `this.ownerId === user.id` and `super.allow*` composition work naturally; unlike `allowRead`, the write paths are asynchronous, so `async` overrides participate in per-record evaluation too. See [Record-Level Access Control for Writes](/reference/v5/database/schema#record-level-access-control-for-writes-record-scoped-allowdelete--allowupdate--allowcreate).
0 commit comments