Roles & Permissions: Two Layers, Seven Org Roles
Platform roles, tenant roles, one shared matrix, and why module access is not decided by whoever last edited their profile name.
Permissions in a multi-tenant ITAD platform have to be precisely right or visibly wrong. There is very little useful middle ground. ReVend OS uses two layers: platform-level roles for the ReVend operator, and organization roles for tenant users.
Platform-level roles
platform_owner can manage the platform end to end. platform_staff can support customers and work across tenants where the support/admin flow allows it. platform_viewer is read-only for oversight. Platform pages check platform scope before they show platform-only tools such as health, package plans or trust-scoring settings.
Org-level roles
Seven tenant roles are used today: org_admin, org_manager, org_warehouse, org_commercial, org_finance, org_operator and org_viewer. They describe the user’s job inside the tenant. Package entitlements decide which modules the tenant has bought; the role decides which of those modules a person may open. Both checks run, in that order, and a tab only appears when both say yes.
One matrix, three readers
Which role may open which module lives in a single matrix in the code, and the module tabs, the sidebar, the command palette and the server-side page guards all read that same matrix. Market, Auction and Sourcing open for admin, manager and commercial. Escrow opens for admin and finance. Finance — invoices, settlements, margins — opens for admin, manager and finance. Testing opens for admin, manager, warehouse and operator. Dashboard, Core, General, Settings and Support are open to every organization role. Platform support bypasses the matrix entirely, because impersonation and support work have to be able to open every page. Before the matrix existed, navigation knew what the tenant had bought and the page guards knew what the role was allowed, and the two never met: a finance user had a clickable Market tab that ended in a bare 404. One matrix, three readers. No more tabs to nowhere.
Hidden, not 404
A module your role may not open is simply not in the navigation. A finance user does not see Market, Auction or Sourcing; a commercial user does not see Escrow. Settings pages for modules the tenant does not have disappear from the menu as well, instead of bouncing to the subscription page. Whoever still lands on such a page through a bookmark, an e-mail link or an old shortcut gets an explanation: which module it is, that this role may not open it, which roles may, and a button back to the dashboard. Tenant-wide settings reserved for the organization admin — billing, business hours, analytics thresholds — stay visible to everyone but open with a “Managed by your administrator” note for other roles. A page that explains itself costs one paragraph; a 404 costs a support ticket.
Operator is the default
Operator is the role an invitation carries unless the inviter picks another one, and it is the role that does the testing work: the test screen is open to admin, manager, warehouse and operator. Contracts go the other way — creating and editing a contract, its services and rates, its required certifications and the signed agreement is reserved for admin, manager and commercial, the same circle that may activate one. Warehouse, operator, finance and viewer read contracts; they see no Edit, Manage or New Contract button, and a direct server call is refused and logged as access denied.
Where enforcement happens
UI navigation is helpful, but the server and database are the adults in the room. Server guards read roles from membership records, not from editable user metadata. RLS keeps tenant data tenant-scoped. Service-role admin code has to add tenant scope explicitly because service role bypasses RLS; that is power with paperwork, not a shortcut.
Admin boundaries
Admin pages are platform-only: platform roles see cross-tenant platform tools, and non-platform users are redirected away from Admin. Tenant profile and team administration live in Settings at /settings/organization-profile and /settings/users. A missing route is annoying; a leaked cross-tenant tool is a fire drill.
Changes and audit
Role and permission changes are audit-sensitive. The platform records who changed access, what changed and why. If a user suddenly sees fewer buttons, support should be able to explain it without reading tea leaves from the browser cache.