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_supportrole 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_supportwithout losingsuper_admin. So owner keepssuper_adminand receives via the existing rule;it_supportis exclusively for a delegate. (This is the trade-off recorded in I-006's scope question.) - The
it_supportpermissions 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_adminshould be able to assignit_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_supportrole as a separate PR.)
Related¶
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; frontmatteruid: T-092βuid: T-095; cross-refs indocs/eop-tasks/tickets/I-007.md(task: T-092βT-095and[T-092](/eop-tasks/tasks/T-092/)β[T-095](/eop-tasks/tasks/T-095/)) anddocs/eop-tasks/tasks/T-084.md([T-092](/eop-tasks/tasks/T-092/)β[T-095](/eop-tasks/tasks/T-095/)); README pointer bumped toT-096. - Why: five in-flight
claude/*branches all heldT-092(mine for theit_supportrole +busy-dirac-QmUdM,t092-deploy-closeout,t092-fontcolor-deployed,wopc-notes-under-fee-type-rrcs79).origin/nightlyhad 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 pernightly'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 todroppedwith the reason + hand-off recorded per the "close the loop on done/dropped" rule. Read the live branch signal first β current setting ismainπ’ Β·nightlyπ΄, so this docs change goes tomainonly, then mirrors totaskboard. - 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 dedicatedit_supportrole 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.
notifyAllMultiChannelwas removed in PR #785; the 5 ex-callers (reconciliation cron, watchdog heartbeat, interactive reconciliation run Γ 3) all now usenotifySystemOwner, which delegates tonotifyRoles(['super_admin'], β¦). As of today the recipient list issuper_adminonly β i.e., just the owner. The watchdog-email leak is closed regardless of whetherit_supportever 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_supportrole exist?" question. If you decide yes, the easy slot to plug into islib/notifications/notify.tsnotifySystemOwnerβ changenotifyRoles(['super_admin'], input)tonotifyRoles(['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 β
droppedmeans 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, frontmattersucceeds: [T-094](/eop-tasks/tasks/T-094/)preserved as historical, and I will not reopen this UID. - Where the
it-supportcapability 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 includesit-supportdirectly, bundlinginfra: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 theit-supportbadge 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_supportANDsuper_adminsimultaneously (the "users hold one role" caveat in the original 2026-06-22 entry). Under the badge model that caveat goes away β the owner can holddirector-erl + super-admin + it-supportif 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.