doc.gno
3.03 Kb · 64 lines
1// Package groups provides Groups containing a base address set plus named
2// Roles, each with their own member set and metadata.
3//
4// A Group is the top-level container — one per DAO, one per permissions
5// instance, etc. A Role is a named subset within a Group with arbitrary
6// per-role metadata.
7//
8// The API separates three concerns explicitly, so each call site picks the
9// right semantic:
10//
11// - base-only operations: Add, Remove, Has, Size, Iterate;
12// - role registry operations: AddRole, GetRole, HasRole, RemoveRole,
13// RoleCount, IterateRoles;
14// - aggregated operations across base + all roles: HasAny, TotalSize,
15// IterateAll, RolesContaining, RemoveFromAll.
16//
17// # Security model
18//
19// A Group, and the *Role values it hands out, are meant to be allocated and
20// held by the consuming realm. Three rules apply at realm boundaries:
21//
22// 1. Do not ACCEPT a *Group or *Role from an external/untrusted caller —
23// subsequent mutations would route to the allocating (attacker)
24// realm's authority, and a poisoned Group could cause DoS or
25// unexpected state.
26//
27// 2. Do not RETURN a *Group or *Role from any method or function callable
28// by untrusted realms. Return *ReadonlyGroup or *ReadonlyRole instead.
29// Exposing a mutable handle is exactly as dangerous as accepting one.
30//
31// 3. Do not TRUST a *ReadonlyGroup or *ReadonlyRole received from an
32// untrusted caller. A readonly view is a live handle over its creator's
33// data, not a snapshot: the sender controls the contents and can mutate
34// them between reads. Base authorization and accounting decisions only
35// on views derived from a Group you allocated yourself.
36//
37// The Readonly() views are the only safe handles to cross a realm boundary —
38// safe to hand out, per rule 3 not blindly safe to consume.
39//
40// # Metadata: do not store mutable pointers
41//
42// Each Role has a free-form "meta any" slot. Meta() returns the stored
43// value as-is, so a pointer stored in meta can be retrieved by an untrusted
44// reader holding a Readonly() view. A direct field write through that
45// pointer is still blocked by the realm-ownership
46// gate, but invoking a MUTATOR METHOD on it (or passing it into a function
47// that mutates by argument) runs under whatever realm allocated it (borrow
48// rule #2) and commits the write. This includes common /p/ types such as
49// *addrset.Set and *avl.Tree — they are mutable pointers, not "just data".
50// Therefore store only:
51//
52// - value types (ints, strings, value structs/slices with NO internal
53// pointer reaching a mutator-bearing type), or
54// - a wrapper whose only exported methods are read-only and which holds no
55// externally-mutable pointer.
56//
57// # Readonly views
58//
59// Group and Role each expose a Readonly() method returning a typed
60// read-only view (ReadonlyGroup, ReadonlyRole; role member sets surface as
61// *addrset.ReadonlySet). The views are concrete structs with unexported
62// fields and only read-side exported methods, so cross-package callers
63// cannot mutate through them.
64package groups