Skip to content

Permissions catalog

Source of truth

The authoritative permissions matrix lives at docs/PERMISSIONS.md. Edit PERMISSIONS.md, not this file. This page is a scan-friendly snapshot for internal staff and contributors who want to look up common permissions quickly.

The drift test (src/test/permissions.matrix.test.ts) parses PERMISSIONS.md and will fail the build if code references a permission not declared there, or if a permission in that file is never referenced in code. Any change to permission wiring must go through the canonical matrix.

See also: Architecture → Permissions system, Architecture → RLS.


Quick lookup — top permissions by domain

The following is a read-only snapshot of the most-frequently-used permissions. For the full matrix (roles, RLS policies, portal scoping, sectional splits) consult PERMISSIONS.md.

Bookings

Permission Meaning
bookings.view List/read bookings
bookings.create Create booking (wizard, import)
bookings.edit Modify booking, add passengers
bookings.delete Soft/hard-delete booking

Customers

Permission Meaning
customers.view List/read customers
customers.create Create customer
customers.edit Edit profile, upload documents
customers.delete Delete customer

Groups

Permission Meaning
groups.view List/read groups
groups.create Create group
groups.edit Reassign passengers, link flights, misc expenses
groups.delete Soft-delete group
group_pricing.edit Save changes on the Pricing tab (rate sheet + tax lines)
group_invoices.view See invoices tab
group_invoices.issue Transition DRAFT → ISSUED (posts revenue + tax journal)

Finance

Permission Meaning
finance.view Access Finance module / reports / vouchers
finance.create Record payments, post journals
finance.edit Edit finance config, reconcile, reverse
finance.payments.record Post a receipt to the ledger
finance.journals.approve Approve a manual journal
finance.journals.approve_own Break-glass self-approval (super-admin only)
finance.tds.deduct Apply TDS on a supplier payment

Approvals

Permission Meaning
approvals.view See the approvals queue
approvals.approve Approve, reject, or send-back bookings

Agents & suppliers

Permission Meaning
agents.view Read B2B partners
agents.create Add partner
suppliers.create Add supplier
suppliers.edit Edit / record transactions

The one rule

Edit PERMISSIONS.md, not this page. Any new catalog entry, role grant change, or sectional split must be made in the canonical matrix so the drift test stays green.


Adding a new permission — 5-step checklist

Mirrors CLAUDE.md and §9 of PERMISSIONS.md.

  1. Add a row to the relevant section in PERMISSIONS.md §6 (page-by-page action matrix).
  2. If the permission is new, add it to §4 (catalog) and §5 (role grants) in the same file.
  3. Write a seed migration under supabase/migrations/ with a timestamped filename (YYYYMMDDHHMMSS_description.sql). The migration must:
    • Insert the permission into the Permission table.
    • Grant it to the correct roles in RolePermission.
    • Be idempotent (IF NOT EXISTS, INSERT … WHERE NOT EXISTS).
  4. Reference the permission in code:
    • Frontend route — <ProtectedRoute requiredPermissions={['x.y']}>
    • Frontend action — <PermissionGate permission="x.y"> around the button/control
    • Backend handler — await requirePermission('x.y') at the top of the route handler in src/lib/api.ts
  5. Run npm test. The drift test (src/test/permissions.matrix.test.ts) fails the build if code references a permission not in the matrix, or vice versa.

When renaming or deleting a permission, update PERMISSIONS.md, every code reference, and add a rename/drop migration — in the same PR.


No role-name shortcuts

CEO, GM, and IT_ADMIN are super-admins because every permission row is seeded for them explicitly (see migration 20260416020000_remove_permission_bypass.sql). They are not super-admins via a hardcoded if role === 'CEO' branch.

  • Do not add if (role === 'CEO') return true anywhere in code or RLS.
  • Do not special-case super-admins in auth_user_has_permission().
  • If someone needs privileged access, grant the permission through a migration.

Revoking a specific row in the PermissionsMatrix admin UI actually restricts CEO/GM/ IT_ADMIN — there is no bypass underneath.


How enforcement works

Permissions are checked at three layers. See Architecture → Permissions system for details.

Layer Mechanism Where
Route <ProtectedRoute requiredPermissions={[…]}> src/App.tsx
UI action <PermissionGate permission="…"> component trees
API await requirePermission('…') src/lib/api.ts (authoritative)
Database RLS policies using auth_user_has_permission('…') Booking, Customer, Agent, Supplier, Payment, JournalEntry, JournalLine, FinanceConfig

The order of precedence inside auth_user_has_permission() is: explicit user-level deny → explicit user-level allow → role grant → otherwise denied.