Admin Panel Access Matrix — updated 02/08/2026
Snapshot as of 02/08/2026. Reflects the actual route gating and server-action guards in the codebase. The table below always shows the current rules — edit rows on the Content page and they update here automatically.
Showing 23 of 23. Filters combine (AND). Click a column's label (or a row's leading cell) to select/highlight it — drag either one past a neighbor to reorder it. Drag a column's right edge (or a row's bottom edge) to resize it, all the way to close it down to a red bar. Click a cell to select it, then drag its blue corner handle across other cells to copy its value into them; right-click any cell to copy its value to the clipboard.
| Resource | Admin | Organizer / Staff | Participant Support | Judge |
|---|---|---|---|---|
Schools saveSchool/deleteSchool carry no role guard — any approved staff-tier role can add, edit, or delete. | Full | Full | Full | Full |
Senseis saveSensei/deleteSensei carry no role guard. | Full | Full | Full | Full |
Referees / Judges (roster) saveReferee/deleteReferee carry no role guard. | Full | Full | Full | Full |
Audience / Spectators No add/delete action exists yet — audience records are created only by self-registration. | Edit payment status | Edit payment status | Edit payment status | Edit payment status |
Participants deleteParticipant blocks Participant Support and Referee (blockCustomerSupport/blockReferee). saveParticipant (edit) has no guard, so Referee can currently edit participant records — narrower than the "view only" intent noted in its own code comment. | Full | Full | Edit — cannot delete | Edit — cannot delete |
Registrations (payment status) updatePaymentStatus blocks Referee; deleteRegistration blocks Participant Support and Referee. | Full | Full | Edit — cannot delete | View only |
Competitions saveCompetition requires requireCompetitionManager (admin/organizer/staff only). | Full | Full | Blocked | Blocked |
Categories (incl. Merge to Mix) saveCategory/deleteCategory/mergeCategoryToMix/mergeCategoryAgeGroup/mergeKataFamily/mergeKataBeltGroup/mergeAdjacentKata/undoLastMerge/undoLastDelete/reorderCategoryGroups/reorderSubcategories all require requireCompetitionManager (admin/organizer/staff only) — this now matches the UI, which already hid these controls from Participant Support and Referee on both /admin/competitions and /kata-categories. | Full | Full | Blocked | Blocked |
Announcements saveAnnouncement/toggleAnnouncement/moveAnnouncement/deleteAnnouncement all require requireContentManager (admin/organizer/staff only). The New/Edit/Delete/Publish/Reorder controls render for Participant Support and Referee anyway, so they see the form and buttons but every submission comes back "Only Admin / Organizer can manage announcements." — the UI should hide these for them instead of letting the action reject. | Full | Full | Blocked | Blocked |
Judging Arena (assign referees/set no. of judges-required) assignRefereeToVideo/unassignRefereeFromVideo/setJudgesRequired/autoAssignReferees require requireJudgingManager (admin/organizer/staff/referee). Participant Support can still watch recordings and see workload, not configure. | Full | Full | View only | Full |
Kata video scoring — submit/edit a score This row is about SUBMITTING a score, not viewing one — see "Kata video scoring — view scores" below for that. Referee scoring is unchanged, enforced by DB RLS (scores_referee_upsert). Admin/Organizer/Staff get an additive override policy (scores_manager_upsert) letting them score any recording as themselves, auto-self-assigning via assign_referee() so they show up correctly wherever assignment drives display. | Full — any recording | Full — any recording | Blocked (cannot submit a score) | Own assigned videos only |
Kata video scoring — view scores Every role can see every recording's individual judge scores and round status (green/red + total once fully judged) on both Kata Arena and this Judging Arena page — opened to everyone in migration 0044 (scores_select_all_authenticated). Participant Support has always had this; only submitting a score is restricted (see the row above). | Full | Full | Full | Full |
Kata recording playback Fixed in migration 0029 — previously no storage.objects SELECT policy existed for the kata-videos bucket, so nobody (including Admin) could actually load a recording. | Full | Full | Full | Full |
Organizer account management (create/edit/delete) createStaffAccount(role=organizer)/saveStaffAccount/deleteStaffAccount all allow admin/organizer/staff, but saveStaffAccount and deleteStaffAccount both reject a non-admin actor whose target is role=admin — an Organizer can manage another Organizer login but never touch the Super Admin's. Creating role=admin isn't possible through this action at all (Supabase dashboard only), so Organizer can never elevate anyone to Admin. | Full | Full — never Admin | Blocked | Blocked |
Classes (students, fee plans, enrollments, invoices) /admin/classes is Admin/Organizer/Staff-only at the route level, same pattern as Accounts and Email Verifications. | Full | Full | Blocked (route) | Blocked (route) |
Participant Records — resend registration confirmation email resendRegistrationConfirmation allows admin/organizer/staff/customer_support. | Full | Full | Full | Blocked |
Participant Support account management (create/edit/delete) createStaffAccount(role=customer_support)/saveStaffAccount/deleteStaffAccount all allow admin/organizer/staff. | Full | Full | Blocked | Blocked |
Accounts page (approvals, invitation codes) /admin/accounts is restricted to Admin and Organizer/Staff at the route level — Participant Support and Referee/Judge are the two roles excluded from the otherwise-shared full nav. approve_profile() and every invitation-code write already run through is_admin(), which has always treated organizer/staff as admin-tier, so Organizer's access here is genuinely full, not just past the door. | Full | Full | Blocked (route) | Blocked (route) |
Email Verifications page /admin/email-verifications is Admin/Organizer-only at the route level, same as Accounts — it can manually mark an account verified, bypassing the sign-in gate. markEmailVerified/resendVerificationEmail already gate on requireAdminTier() (admin/organizer/staff), so no action-level change was needed once the route let Organizer through. | Full | Full | Blocked (route) | Blocked (route) |
Commissions, Winner Rewards, Other Payouts & Profit/Loss page /admin/commissions is Admin/Organizer/Staff-only at the route level, same pattern as Classes. setCommissionPayoutStatus/setWinnerPayoutStatus/uploadCommissionReceipt/uploadWinnerReceipt/saveOtherPayout/deleteOtherPayout/setOtherPayoutStatus/uploadOtherPayoutReceipt all require requireCompetitionManager. Other Payouts is a manually-entered ledger (vendor payments, refunds, bonuses, etc.) with its own Add/Edit/Delete, same Paid/Unpaid + receipt pattern as Commissions/Rewards. Profit/Loss is a per-tier report (participant fee revenue minus every Paid commission/reward/other-payout for that tier) generated on demand -- nothing here is stored, all computed live. | Full | Full | Blocked (route) | Blocked (route) |
Content page (announcements, competition tiers, access matrix, and every other reference table) canManage (admin/organizer/staff) gates every create/edit/delete control and CSV uploader across all six sections; canView additionally admits Participant Support and Referee/Judge, who see every table's live data with inputs disabled and no Save/Delete/+Add row/upload controls rendered at all. | Full | Full | View only | View only |
CSV bulk upload — Schools / Senseis / Referees / Audience / Participants / Announcements / Invitation Codes / Content reference tables Every one of these bulk-upload actions calls the shared bulkUploadRoleError guard, which restricts to admin/organizer only — Participant Support and Referee are rejected. This is narrower than the single-record Add/Edit/Delete forms next to each uploader (see their own rows above), which stay open to all four. | Full | Full | Blocked | Blocked |
CSV bulk upload — Organizer / Support accounts Same authorization as single-account creation, checked per-upload. | Full | Full | Blocked | Blocked |
