Skip to content

T 095


uid: T-095 title: RBAC β€” introduce it_support role to scope infra/integrity alerts independently of admin status: dropped area: rbac created: 2026-06-22 updated: 2026-06-23 succeeds: T-094


RBAC sequence: succeeds T-094 β€” adding the it_support role needs T-094's people-roster + role-assignment layer to assign it. See the sequence map in T-022. (Marked by EOP Local Assistance, 2026-06-23.)

Why

I-006 surfaced that infra/integrity alerts (notifyAllMultiChannel) reached all active admins (me@jefferochan.com + jake@establishrecords.com + alisonhytang@gmail.com), when only the owner can act on a broken cron / imbalanced JE / failed deploy. The immediate fix in that ticket narrowed those 5 call sites to notifyRoles(['super_admin'], …) via a new notifySystemOwner helper β€” but super_admin is structurally the owner-of-everything role, not a receives-infra-alerts role.

Owner direction (verbatim, 2026-06-22 β€” via AskUserQuestion follow-up):

"Create a role as IT Support?"

The aim is a dedicated it_support role so the owner can later delegate infra/integrity alerts to a non-admin IT contact without granting them accounting / project / user-management permissions. Today the owner is the only super_admin, so the immediate behaviour is identical; the value is enabling future delegation without a code change.

Scope

RBAC extension across: - lib/rbac/types.ts β€” add 'it_support' to InternalRole union + ACTIVE_USER_ROLES. - lib/rbac/types.ts β€” ROLE_PERMISSIONS['it_support'] β€” minimal: infra alert read access only. Decision needed: should it_support also get audit:read? Probably yes β€” they may need to inspect logs to triage. NOT users:write / projects:write / transactions:write. - lib/notifications/notify.ts β€” change notifySystemOwner from notifyRoles(['super_admin'], …) to notifyRoles(['super_admin', 'it_support'], …). Owner still receives (holds super_admin); an IT contact assigned it_support would also receive. - pages/admin/users/* (or wherever role assignment lives) β€” surface it_support in the role picker. - Firebase custom claims sync (if applicable) β€” verify it_support flows through the claims set so middleware / API guards see the new role. - Seed / migration β€” no existing user should automatically become it_support; assignment is owner-driven.

Caveats

  • Users hold one role, not many β€” owner cannot self-assign it_support without losing super_admin. So owner keeps super_admin and receives via the existing rule; it_support is exclusively for a delegate. (This is the trade-off recorded in I-006's scope question.)
  • The it_support permissions list must be narrow enough that handing it to a future IT contractor doesn't accidentally grant them accounting / user-management surface. Keep it strictly read until a concrete need for write emerges.
  • The role-assignment UI may need its own minimal permission gate (only super_admin should be able to assign it_support, since it grants visibility into infrastructure alerts).

Out of scope (deliberate)

  • Re-broadening the audience for any specific alert β€” the per-call-site narrowing in I-006 (5 callers β†’ notifySystemOwner) is intentional and stays. T-092 only widens the role audience for that helper.
  • A general permissions-overhaul β€” this task ships exactly one new role.

Decision log

2026-06-22 β€” task opened (owner asked for the role separately from the leak fix)

  • βœ… Attestation (Accounting [Infrastructure Development]): read AGENTS.md; checked the board by scope β€” no existing RBAC task covers a new role addition (T-083 is auth domain-proxy work, not role-model extension). Opening T-092 as the structural follow-up to I-006.
  • Source: Accounting [Infrastructure Development] Β· https://claude.ai/code/session_015P6KzVYsQCLgEmUjR9bMwM
  • Proposed by: the owner. Approved by: the owner (same prompt).
  • Evidence β€” owner, 2026-06-22 (verbatim):

    "Create a role as IT Support?"

(Via AskUserQuestion: "super_admin now + T-092 later" β€” fix the leak today, design the it_support role as a separate PR.)

I-006 (the originating ticket) Β· T-084 (the cron migration whose watchdog firing surfaced the leak) Β· lib/rbac/types.ts Β· lib/notifications/notify.ts.

2026-06-23 β€” renumbered T-092 β†’ T-095 (UID collision at merge)

  • βœ… Attestation (Accounting [Infrastructure Development]): read AGENTS.md; following the "UID collisions across parallel branches β€” renumber on merge" rule.
  • Source (edit): Accounting [Infrastructure Development] Β· https://claude.ai/code/session_015P6KzVYsQCLgEmUjR9bMwM
  • What changed: git mv docs/eop-tasks/tasks/T-092.md β†’ T-095.md; frontmatter uid: T-092 β†’ uid: T-095; cross-refs in docs/eop-tasks/tickets/I-007.md (task: T-092 β†’ T-095 and [T-092](/eop-tasks/tasks/T-092/) β†’ [T-095](/eop-tasks/tasks/T-095/)) and docs/eop-tasks/tasks/T-084.md ([T-092](/eop-tasks/tasks/T-092/) β†’ [T-095](/eop-tasks/tasks/T-095/)); README pointer bumped to T-096.
  • Why: five in-flight claude/* branches all held T-092 (mine for the it_support role + busy-dirac-QmUdM, t092-deploy-closeout, t092-fontcolor-deployed, wopc-notes-under-fee-type-rrcs79). origin/nightly had already landed its own T-092 (Restore the Claude↔NAS root-exec channel, opened by Records (Infrastructure)). This PR's content was the second to merge, so per AGENTS.md it renumbers; T-095 is the next-free per nightly's README and the lowest UID free past all in-flight PRs visible at audit time.
  • Immutable history preserved: prior dated entries + commit messages on this branch still reference T-092 (notably the verbatim AskUserQuestion quote: "super_admin now + T-092 later"). Per AGENTS.md, only forward references move; the historical quote stays as-is.

2026-06-23 β€” DROPPED β€” handed off to User Management (RBAC complete makeover in flight)

  • βœ… Attestation (Accounting (Infrastructure)): read AGENTS.md; closing the loop on the task I authored by flipping it to dropped with the reason + hand-off recorded per the "close the loop on done/dropped" rule. Read the live branch signal first β€” current setting is main 🟒 Β· nightly πŸ”΄, so this docs change goes to main only, then mirrors to taskboard.
  • Source: Accounting (Infrastructure) Β· https://claude.ai/code/session_015P6KzVYsQCLgEmUjR9bMwM
  • Owner direction (verbatim, 2026-06-23, this session):

    "I'm actually discussing with the User Management agent to do a complete makeover, so drop T-095 (leave a note telling the User Management agent that it's free to be handed over to)"

  • Why dropped: the owner is working with User Management (https://claude.ai/code/session_01GGT5n9vCxKWoUQSRAfMSiW, cf. docs/agents-registry.md) on a complete RBAC makeover β€” User Management has been the primary RBAC agent this period (PR #420 user-management audit, PR #424 Firestore-backed page rules, PR #436 multi-provider register flow, PR #437 register-page leak fix, PR #438 iCloud-to-password routing). Adding a single dedicated it_support role in isolation would lock in a shape User Management is about to revisit holistically; better that any "infra-alert recipient" abstraction lands as part of their makeover, not as a stand-alone bolt-on now.
  • Important: the originating problem (I-007) is already SHIPPED and FIXED.
  • notifyAllMultiChannel was removed in PR #785; the 5 ex-callers (reconciliation cron, watchdog heartbeat, interactive reconciliation run Γ— 3) all now use notifySystemOwner, which delegates to notifyRoles(['super_admin'], …). As of today the recipient list is super_admin only β€” i.e., just the owner. The watchdog-email leak is closed regardless of whether it_support ever ships.
  • The only thing this task would have added on top is a dedicated non-admin role for delegating infra alerts to a future IT contractor without granting them accounting / users / projects access. If the makeover preserves that delegation goal, the takeaway below covers it.
  • Hand-off note to User Management (the agent picking this up):

    Your makeover is about to subsume the "should an it_support role exist?" question. If you decide yes, the easy slot to plug into is lib/notifications/notify.ts notifySystemOwner β€” change notifyRoles(['super_admin'], input) to notifyRoles(['super_admin', 'it_support'], input) (or whatever your new role-name shape uses) and infra-alert routing follows. The 5 ex-broadcast callers don't need to know. If you decide no (super_admin remains the sole recipient), no code change is needed β€” the current routing is already correct for that decision. Either way, don't reopen T-095 β€” pick the call up inside whatever umbrella task your makeover lives under. T-095 stays dropped as a record that this specific design was deferred to your work.

  • Blast radius: zero β€” dropped means no code/data/schema change from this task. The I-007 recipient-scope fix that this task was a structural follow-up to remains in effect on prod.
  • Related: I-007 (the originating leak, fixed), T-084 (the deploy that surfaced the leak, done), docs/agents-registry.md (the User Management ledger row).

2026-06-23 β€” hand-off acknowledged; it-support becomes a badge inside T-022

  • βœ… Attestation (User Management): read AGENTS.md; per the cross-agent-edit rule appending this acknowledgement rather than reopening / re-titling the dropped task.
  • Source: User Management Β· https://claude.ai/code/session_01GGT5n9vCxKWoUQSRAfMSiW
  • Accepting the drop: the explicit hand-off from Accounting (Infrastructure) above is acknowledged. T-095 stays dropped, frontmatter succeeds: [T-094](/eop-tasks/tasks/T-094/) preserved as historical, and I will not reopen this UID.
  • Where the it-support capability lives now: inside T-022's expanded scope β€” the badge-based RBAC model the owner approved on 2026-06-23. The starter badge registry T-022 introduces includes it-support directly, bundling infra:dmarc:read@all + notifications:infra:read@all. The owner's question Accounting (Infrastructure) flagged ("does the owner want a delegate audience for infra alerts?") gets resolved by whether the owner ever grants the it-support badge to a non-self user β€” yes β†’ delegate; no β†’ behaviour is byte-identical to today. Either decision needs no code change in T-022's path, only a badge assignment.
  • Why this is the better resting place: under the old single-axis model the owner couldn't hold it_support AND super_admin simultaneously (the "users hold one role" caveat in the original 2026-06-22 entry). Under the badge model that caveat goes away β€” the owner can hold director-erl + super-admin + it-support if they want, receiving infra alerts directly without a delegate. The drop + fold is therefore not just paperwork β€” it removes a constraint the standalone task couldn't avoid.
  • Blast radius: zero β€” same as the drop entry above. T-095 stays a historical record of the design that was deferred; the badge work lives in T-022's PR sequence.