Part of Stem. This page defines readers, the one computed set that every read, every sync answer and every listing is filtered by.
The readers of a node are the principals that hold read or higher over it through the authority graph, computed by one recursion over the node's access mode, the live grants whose subject covers it, its parent's readers and, for target mode, the readers of the node it targets.
Readers are computed, never written. A grant is a signed input; readers are the output of evaluating all the grants and revocations a peer holds. Two peers with the same blobs compute the same readers for every node. The daemon materialises the result per node so that the question "may principal P read blob B" is a join, not a graph walk, at request time.
The recursion
Let live(n) be the set of live grants, after the two-pass evaluation, whose subject is n or an ancestor of n with exact false, at access read or higher. Let members(a) expand an audience to principals: everyone is the universal set; key is one principal; group is the principals holding a live grant at read or higher whose subject is that group; bearer is the pseudo-principal "holder of that secret"; readers is the readers of the named node, computed by this same recursion. Let admins(space) be the owner plus every principal holding live admin over the space root.
Then, for a node n in a space:
readers(n) =
admins(space)
∪ ⋃ { members(g.audience) : g ∈ live(n) }
∪ parentTerm(n)
parentTerm(n) =
readers(parent(n)) if n.access = inherit and n ≠ root
∅ if n.access = own, or n = root
readers(target(n)) if n.access = targetNotes on the terms:
live(n) already includes grants on ancestors, because a grant's subject covers its subtree unless exact. So the parentTerm for inherit adds only what the parent gets from its access mode chain, which matters when an ancestor is itself in target mode or has grants with exact set.
everyone in any term makes readers(n) universal; the node is public. A public node under an own child does not make the child public.
target(n) is the node named by the state field the kind's target pointer selects. If that node is unknown locally, readers(target(n)) is admins(space) only, and the comment is readable by nobody else until the target arrives. This fails closed.
Cycles cannot arise through inherit because the placement tree is acyclic. A cycle through readers audiences or target modes is broken by treating the repeated node as ∅ on the second visit.
Writers are readers: write and admin imply read in the level order, so live(n) at read or higher includes them.
From readers to blobs
Every blob the handler indexes is mapped to the nodes whose state it belongs to: a Node blob to its node; a Change or Snapshot to every node whose head target reaches it; a file blob to every node whose state embeds it; a Grant, Revocation or Group to the node its subject covers (or, for a group, to every node that has a grant to that group). A blob is readable by principal P if P is in the readers of any node it maps to. This is the blob_access relation in Database structure. A blob that maps to no node (an orphan file, a stashed Change) is readable by nobody but the peer that holds it.
What readers are used for
A peer answering Reconcile or Fetch keeps only blobs whose nodes have the caller's authenticated accounts among their readers.
A web server answering an HTTP read applies the same test to the request's bearer identity.
Listings, search, citations and feeds filter by the same join. There is no second rule.
Every serve that passes the test is written to the disclosure ledger with the grant that made it pass as its basis.
Today (HM24)
The blob_visibility table holds (blob, space) rows: space = 0 means public, space = N means readable by the owner of N, its writers and its site. That table is already a materialised readers relation with two hard-coded audiences, as the Permissions System investigation observed. Stem makes the audience general and the derivation explicit, and closes the gaps the investigation listed: HTTP and peer sync disagreeing on who is a reader, comment visibility frozen at creation, and private-by-path rather than private-by-grant.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime