docs: RBAC grant model contract (S2 contract 2) #1436

Merged
fred merged 2 commits from contract/rbac-grants into next 2026-08-28 19:09:19 +00:00
Collaborator

S2 contract 2 of 9: the RBAC grant model the hierarchy schema contract (#1435) attaches to.

Defines:

  • Three authority layers, none substitutable: platform role (better-auth member/admin), hierarchy grants, workspace membership (SOT REQ-ID-001) — §1
  • Vocabulary: viewer | member | owner, one totally-ordered set at every level, constraint-checked — §2
  • Evaluation: deny-by-default, down-the-chain only, effective role = max over user-direct and team-conferred grants on ancestors, live fail-closed evaluation — §3
  • Grant management: owner-managed, no self-escalation, node creation confers no automatic grant (explicit initial owner in the same audited operation) — §4
  • Transfer authority: both-sides effective owner, completing contract 1 §4.2 — §5
  • Revocation propagation: row deletion is the revocation; next-decision bound plus the 30s/next-message socket bound shared with identity §7.1 — §6
  • Verification matrix extending A1 §8.3 acceptance 2–3 and contract 1 §6 — §7

Embedded decision for Jason's ruling: platform admin confers no implicit tenant access (§1.1).

Sources: PRD Part I §4; native-kanban SOT Amendment A1 §8.1.3/§8.3; hierarchy-schema contract (PR #1435); identity-lifecycle contract §1.4 (PR #1433).

Independent review (gate 16) to follow before merge. No self-merge without review.

S2 contract 2 of 9: the RBAC grant model the hierarchy schema contract (#1435) attaches to. Defines: - Three authority layers, none substitutable: platform role (better-auth member/admin), hierarchy grants, workspace membership (SOT REQ-ID-001) — §1 - Vocabulary: viewer | member | owner, one totally-ordered set at every level, constraint-checked — §2 - Evaluation: deny-by-default, down-the-chain only, effective role = max over user-direct and team-conferred grants on ancestors, live fail-closed evaluation — §3 - Grant management: owner-managed, no self-escalation, node creation confers no automatic grant (explicit initial owner in the same audited operation) — §4 - Transfer authority: both-sides effective owner, completing contract 1 §4.2 — §5 - Revocation propagation: row deletion is the revocation; next-decision bound plus the 30s/next-message socket bound shared with identity §7.1 — §6 - Verification matrix extending A1 §8.3 acceptance 2–3 and contract 1 §6 — §7 Embedded decision for Jason's ruling: platform admin confers no implicit tenant access (§1.1). Sources: PRD Part I §4; native-kanban SOT Amendment A1 §8.1.3/§8.3; hierarchy-schema contract (PR #1435); identity-lifecycle contract §1.4 (PR #1433). Independent review (gate 16) to follow before merge. No self-merge without review.
fred added 1 commit 2026-08-26 23:23:36 +00:00
docs: RBAC grant model contract (S2 contract 2)
ci/woodpecker/pr/ci Pipeline is pending
3538d33106
Defines the hierarchy grant vocabulary (viewer|member|owner, totally
ordered), evaluation semantics (deny-by-default, down-the-chain, max rule,
live fail-closed evaluation), grant management (owner-managed, no
self-escalation, explicit bootstrap of authority), transfer authority
(both-sides owner, completing contract 1 §4.2), and revocation propagation
with the same 30s/next-message bound as identity §7.1.

Embedded decision for ratification: platform admin confers no implicit
tenant access (§1.1).

Sources: PRD Part I §4, native-kanban SOT Amendment A1 §8.1.3/§8.3,
hierarchy-schema contract (PR #1435), identity-lifecycle contract §1.4
(PR #1433).
fred added 1 commit 2026-08-26 23:33:11 +00:00
- name the two shipped platform-admin bypass paths (command-authorization
  hasScope short-circuit, MCP tenant-admin scope derivation) as
  non-conformant; 7.4 retires them explicitly
- suspend team grant subjects pending a team contract; command surface
  refuses team-subject grants; evaluation semantics pre-specified
- no-self-escalation restated with future-decoupling rationale; observable
  is an audit invariant, not an unconstructible refusal test
- bootstrap seeding scoped to top-level company creation; child nodes
  inherit via down-the-chain, resolving the 7.7/4.3 contradiction
- exact A1 quote (not a bypass of workspace authorization)
- subject/role audit fields attributed as this contract's addition
- principal-position consequence named (1.3) with matrix cell
- added observables: zero membership rows, fail-closed fault injection,
  no existence oracle
- role-string namespacing rule (4.5)
- ruling request names the PRD-admins interpretive resolution
rev-code-01 approved these changes 2026-08-26 23:44:45 +00:00
rev-code-01 left a comment
Member

APPROVED — rev-code-01, pinned to head 501112d2a4e8e14d4e12d2a8fc67f9d7fb713d29 (re-verified unmoved immediately before posting).

Scope: doc-only, single file docs/requirements/rbac-grant-model.md (+259 lines, two commits). Verified independently of the GLM 5.3 r1/r2 model reviews; every codebase-factual claim measured by me at this head:

  1. §1.1(a) admin bypass — command layer: confirmed exact. command-authorization.service.ts:158 hasScope body is if (role === 'admin') return true; — every command scope allowed for platform admin, no grant consulted.
  2. §1.1(b) admin bypass — MCP: confirmed exact. mcp.service.ts:98-99 deriveMcpToolScopesForUser maps role adminTENANT_ADMIN_MCP_SCOPES, which includes brain_create_task / brain_update_task (task create/update, as the contract states).
  3. §1.1 AdminGuard claim: confirmed — the guard's platform-role test is userRole !== 'admin' → Forbidden (admin.guard.ts:111); that check is the platform role's only meaning, as stated.
  4. §4.5 third vocabulary: confirmed — CommandRole = 'admin' | 'member' | 'viewer' (command-authorization.service.ts:8), colliding strings with users.role and the new hierarchy set; the namespacing rule is motivated by a real collision.
  5. §1.4 legacy teams table: confirmed — teams (schema.ts:127) carries ownerId/managerId authority columns, no workspace binding; the suspension rationale is factual.
  6. Source quotes exact: PRD Part I §4 line 93-94 ("admins restrict access per company, estate, and project; grants are evaluated down the chain"); SOT A1 §8.1.3 line 411 ("not a bypass of workspace authorization") with the surrounding "chain adds where grants can be declared" context; A1 §8.3 acceptance 2-3 ("exactly the workspace permissions the chain implies", "no cross-tenant existence oracles"); REQ-TEN-001 line 126 (principals require ACTIVE workspace membership); REQ-ID-001 (workspace-bound identity scope); users.role text default member.
  7. Contract-1 cross-references verified against PR #1435's ref: §3.1 reserves team_id → teams.id in the exactly-one-of CHECK with role vocabulary "owned by contract 2 §2"; §4.2's "authority over BOTH" is what §5 completes; §5.2's audit event enumerates actor/verb/target(+transfer parents) and does NOT enumerate subject/role — §4.4's "this contract's addition" claim is exact. Contract 1 rev 3 is explicitly co-versioned against this contract's revision 2 at this exact head 501112d2 — mutual consistency is pinned from both sides.

Gates run by me in a fresh worktree at this head: format:check rc=0 (pinned prettier; both commits content-only, parent also clean — this PR needed no style commit; the check's ability to discriminate was demonstrated this session on identical commands); web-deps build rc=0; typecheck rc=0 (45/45 incl. preflight); lint rc=0 (25/25).

Document quality: every MUST has a bounded §7 observable; §4.2's no-self-escalation observable is honestly downgraded to the §7.7 audit-stream invariant with the reason stated (under the three-role vocabulary the rule is unviolable, so a refusal test cannot exist yet); the r2 model review's NEW-1 (top-level company creation authority not answerable from the text) is real but fails closed and is recorded for contract 3 — agreed non-blocking. The embedded §1.1 ruling (platform admin confers no implicit tenant access) and the PRD-"admins" interpretive resolution are both surfaced explicitly for ratification, not silently decided.

Non-blocking notes:

  • [S1] Cross-references to docs/requirements/hierarchy-schema.md (#1435) and identity-lifecycle.md (#1433) dangle at this head — both sibling PRs are open. Deliberate sequencing, but merge order should land the referenced contracts with (or before) this one, or land them together, so a fresh clone of next never carries dead links.
  • [S2] NEW-1 from the r2 review stays open for contract 3 (wizard/top-level creation authority attribution) — worth an explicit entry in that contract's brief.

CI note: 2819 running on this head at review time. Merge waits on 2819 terminal green.

**APPROVED — rev-code-01, pinned to head `501112d2a4e8e14d4e12d2a8fc67f9d7fb713d29`** (re-verified unmoved immediately before posting). Scope: doc-only, single file `docs/requirements/rbac-grant-model.md` (+259 lines, two commits). Verified independently of the GLM 5.3 r1/r2 model reviews; every codebase-factual claim measured by me at this head: 1. **§1.1(a) admin bypass — command layer:** confirmed exact. `command-authorization.service.ts:158` `hasScope` body is `if (role === 'admin') return true;` — every command scope allowed for platform admin, no grant consulted. 2. **§1.1(b) admin bypass — MCP:** confirmed exact. `mcp.service.ts:98-99` `deriveMcpToolScopesForUser` maps role `admin` → `TENANT_ADMIN_MCP_SCOPES`, which includes `brain_create_task` / `brain_update_task` (task create/update, as the contract states). 3. **§1.1 AdminGuard claim:** confirmed — the guard's platform-role test is `userRole !== 'admin'` → Forbidden (`admin.guard.ts:111`); that check is the platform role's only meaning, as stated. 4. **§4.5 third vocabulary:** confirmed — `CommandRole = 'admin' | 'member' | 'viewer'` (`command-authorization.service.ts:8`), colliding strings with `users.role` and the new hierarchy set; the namespacing rule is motivated by a real collision. 5. **§1.4 legacy teams table:** confirmed — `teams` (schema.ts:127) carries `ownerId`/`managerId` authority columns, no workspace binding; the suspension rationale is factual. 6. **Source quotes exact:** PRD Part I §4 line 93-94 ("admins restrict access per company, estate, and project; grants are evaluated down the chain"); SOT A1 §8.1.3 line 411 ("not a bypass of workspace authorization") with the surrounding "chain adds where grants can be declared" context; A1 §8.3 acceptance 2-3 ("exactly the workspace permissions the chain implies", "no cross-tenant existence oracles"); REQ-TEN-001 line 126 (principals require ACTIVE workspace membership); REQ-ID-001 (workspace-bound identity scope); `users.role` text default `member`. 7. **Contract-1 cross-references** verified against PR #1435's ref: §3.1 reserves `team_id → teams.id` in the exactly-one-of CHECK with role vocabulary "owned by contract 2 §2"; §4.2's "authority over BOTH" is what §5 completes; §5.2's audit event enumerates actor/verb/target(+transfer parents) and does NOT enumerate subject/role — §4.4's "this contract's addition" claim is exact. Contract 1 rev 3 is explicitly co-versioned against this contract's revision 2 at this exact head `501112d2` — mutual consistency is pinned from both sides. Gates run by me in a fresh worktree at this head: `format:check` rc=0 (pinned prettier; both commits content-only, parent also clean — this PR needed no style commit; the check's ability to discriminate was demonstrated this session on identical commands); web-deps build rc=0; `typecheck` rc=0 (45/45 incl. preflight); `lint` rc=0 (25/25). Document quality: every MUST has a bounded §7 observable; §4.2's no-self-escalation observable is honestly downgraded to the §7.7 audit-stream invariant with the reason stated (under the three-role vocabulary the rule is unviolable, so a refusal test cannot exist yet); the r2 model review's NEW-1 (top-level company creation authority not answerable from the text) is real but fails closed and is recorded for contract 3 — agreed non-blocking. The embedded §1.1 ruling (platform admin confers no implicit tenant access) and the PRD-"admins" interpretive resolution are both surfaced explicitly for ratification, not silently decided. Non-blocking notes: - [S1] Cross-references to `docs/requirements/hierarchy-schema.md` (#1435) and `identity-lifecycle.md` (#1433) dangle at this head — both sibling PRs are open. Deliberate sequencing, but merge order should land the referenced contracts with (or before) this one, or land them together, so a fresh clone of `next` never carries dead links. - [S2] NEW-1 from the r2 review stays open for contract 3 (wizard/top-level creation authority attribution) — worth an explicit entry in that contract's brief. CI note: 2819 running on this head at review time. Merge waits on 2819 terminal green.
fred merged commit f8e1b43b5b into next 2026-08-28 19:09:19 +00:00
Sign in to join this conversation.