# RBAC Grant Model Contract Status: DRAFT — awaiting ratification (webui-audit S2, contract 2 of 9). Authority: PRD Part I §4 ("Granular RBAC: admins restrict access per company, estate, and project; grants are evaluated down the chain") and the native-kanban SOT Amendment A1 (§8.1.3 RBAC evaluation, §8.3 acceptance 2). This document defines the grant vocabulary, evaluation semantics, and revocation propagation that the hierarchy schema contract (`docs/requirements/hierarchy-schema.md`, contract 1) attaches to. Contract 1 pins the `hierarchy_grants` table shape and defers the `role` vocabulary and the meaning of "authority" here; the identity contract (`docs/requirements/identity-lifecycle.md` §1.4) pins that account creation grants nothing. Scope: the roles that can appear in `hierarchy_grants.role`, what a grant at each hierarchy level confers, how grants evaluate down the chain, how revocation propagates, and who may manage grants. Out of scope: the hierarchy tables themselves (contract 1), workspace-internal membership and its role/capability vocabulary (native-kanban SOT REQ-ID-001 and its implementing schema), roll-up projection semantics (contract 8), wizard seeding (contract 3). ## 1. Three authority layers, none substitutable 1. **Platform role** (`users.role`, better-auth: `member` | `admin`) governs instance administration — user management, system settings, provider configuration. It is not tenancy authority: holding platform `admin` confers **no implicit hierarchy grant and no workspace authorization**. An operator who should see tenant content holds an explicit, audited grant like anyone else. (This is the deny-by-default consequence of A1 §8.1.3's "never a bypass" rule; today's `AdminGuard` checks `role === 'admin'` for admin endpoints and that stays its only meaning.) 2. **Hierarchy grants** (`hierarchy_grants`, contract 1 §3) declare tenancy authority at company, estate, or platform-project scope and evaluate down the chain to workspace-scoped authorization (§3 below). 3. **Workspace membership** (SOT REQ-ID-001) remains its own mechanism. A chain grant confers command authorization over descendant workspaces; it does not create membership rows, and row-level principal positions (task owner, proposer, decision actor) still require ACTIVE workspace membership exactly as REQ-TEN-001/REQ-ID-001 acceptance states. ## 2. Role vocabulary One vocabulary at every hierarchy level, totally ordered — a higher role includes everything below it: 1. `viewer` — read: sees the node, its subtree structure, and the roll-up aggregates over descendant workspaces (within contract 8's carve-out bounds); read access to descendant workspace content per the SOT's read command families. No mutation of anything. 2. `member` — work: everything `viewer` has, plus write authorization for business/orchestration command families in descendant workspaces (the concrete command-family mapping is implementation work under SOT REQ-ID-001; this contract pins that `member` maps to the workspace write families and nothing structural). 3. `owner` — structure: everything `member` has, plus hierarchy mutations on the subtree (create/rename/delete child nodes, transfers per §5), and grant management on the node and its subtree (§4). No other value is valid in `hierarchy_grants.role`; the column is constraint-checked against exactly these three. Extending the vocabulary is a contract amendment, not an implementation decision. ## 3. Evaluation semantics 1. **Deny by default.** No grant on any ancestor → no authority. There are no implicit grants: not from platform role (§1.1), not from creating a node (§4.3), not from workspace membership (membership without a chain grant confers exactly what the SOT's own membership rules confer inside that workspace, nothing up the chain). 2. **Down-the-chain only.** A grant on a node applies to that node and its entire descendant subtree. Nothing evaluates upward or sideways: a grant on an estate says nothing about the parent company or sibling estates. 3. **Effective role = maximum.** A subject's effective role at any node is the highest role among: grants held directly by the subject's user on that node or any ancestor, and grants held by any team the user is a member of on that node or any ancestor. Roles never subtract — there is no negative/deny grant in this model; revocation is deletion (§6). 4. **Team grants follow live membership.** A team grant confers its role on the team's current members, evaluated at decision time. Leaving the team is loss of the grant with §6's propagation bound. 5. **Live evaluation, fail closed.** Authorization decisions derive from the live grant and team-membership rows (or from a cache that is invalidated in the same transaction as any grant/membership/hierarchy mutation). A decision path that cannot read grant state denies. No materialized ACL is ever authoritative. 6. **Tenant context stays derived from authenticated authority** (REQ-TEN-001). The chain adds where grants can be declared; a workspace request is still authorized against that workspace, with the chain contributing the effective role — never a bypass of workspace-scoped checks. ## 4. Grant management 1. Creating, changing, or revoking a grant on a node requires effective `owner` on that node (directly or via any ancestor). 2. **No self-escalation.** A grant manager cannot create a grant with a role higher than their own effective role on the target node. (With the §2 vocabulary this only bites managers acting through team-conferred `owner`: the rule is stated so it survives vocabulary amendments.) 3. Creating a hierarchy node confers no automatic grant. Bootstrap of authority is explicit: the creating command names the initial `owner` grant in the same audited operation, and the wizard (contract 3) seeds the first company's initial `owner` the same way. 4. Every grant mutation is a semantic audit event per contract 1 §5.2 (actor, verb, subject, target, role). ## 5. Transfer authority (completes contract 1 §4.2) "Authority over BOTH the source and the destination parent" means: effective `owner` on the current parent node (or an ancestor) AND effective `owner` on the destination parent node (or an ancestor), evaluated at transfer time in the transfer's own transaction. One subject must hold both; two cooperating half-authorized subjects are not a transfer protocol this contract defines. ## 6. Revocation propagation 1. Revoking a grant (deleting the row), removing a user from a team that carries a grant, or deleting an ancestor node's grant during node deletion (contract 1 §3.3) all propagate identically: the authority derived from that grant is gone for every descendant workspace. 2. **Bound:** the next authorization decision on any affected transport decides against the revoked grant. Concretely: no new HTTP/MCP command authorized by the revoked grant after the revoking transaction commits; an open Socket.IO connection whose subscriptions depend on the revoked grant is re-evaluated within 30 seconds or at its next inbound message, whichever comes first (same bound as the identity contract's §7.1 deactivation rule; same mechanism may serve both). 3. Revocation is subtractive only in effect, not in representation: the evaluator never needs tombstones; deletion of the row is the revocation. ## 7. Verification requirements Binding on the implementing PRs (extends A1 §8.3 acceptance 2–3 and contract 1 §6.4): 1. Vocabulary: the role CHECK constraint rejects any value outside `viewer|member|owner` (real-PostgreSQL witness, `ci-postgres` service in the `test` CI step). 2. Per-level conferral: for each of the three levels × three roles, a grant yields exactly the implied workspace authorization in a descendant workspace and nothing in a non-descendant workspace (the A1 §8.3 "exactly the permissions the chain implies" matrix, enumerated). 3. Ordering: `owner` ⊇ `member` ⊇ `viewer` behaviorally — each higher role passes every lower role's positive cases. 4. Deny-by-default: platform `admin` with no grant reaches no tenant content; workspace member with no chain grant gains nothing outside SOT membership semantics; fresh account reaches nothing (identity contract §1.4 cross-check). 5. Max-rule and teams: user-direct and team-conferred grants combine to the maximum; team-leave drops authority within the §6.2 bound; decision-time evaluation witnessed (grant added → next decision allows; no restart or re-login required). 6. Revocation: each revocation path in §6.1 denies the next command on every transport; the socket bound is measured; a cached-authorization implementation proves transactional invalidation (grant revoked and decision made on two distinct physical connections). 7. Grant management: non-`owner` cannot mutate grants; self-escalation attempt refused; node creation without the explicit initial grant refused; every mutation produces its audit event. 8. Transfer: both-sides `owner` accepted, each single-side case refused (completing contract 1 §6.2). ## Ruling request Ratify sections 1–7 as written, with one decision embedded: platform `admin` confers no implicit tenant access — operators see tenant content only through explicit, audited grants (§1.1) — say "agreed" or name the implicit access you want platform admins to have.