Separate membership from permission
Tenant membership answers where an identity may operate. Roles and permissions answer what actions are permitted inside that boundary. Keeping the concepts separate makes product rules easier to review and test.
Permission names should describe stable business actions rather than individual buttons or routes. Interface changes then do not silently redefine the authorization model.
Enforce every protected action
The Go API rechecks authorization for protected operations. A hidden button, disabled form, or protected Next.js route improves the user experience but is never the security decision.
Background jobs, support tools, and integrations need the same explicit authorization and tenant rules as browser-driven requests.
Bound delegated administration
Tenant administrators can manage access only within the delegation rules assigned to them. Role inheritance and administration need cycle prevention, privilege ceilings, and tests for attempted escalation.
Global operators remain a separate operational boundary rather than an invisible super-role inside every customer tenant.
Make policy changes auditable
Role definitions, permission catalogs, assignments, and authorization-sensitive changes should produce evidence that operators can inspect. Append-only policy events help explain how access changed over time.
Platform keeps the authorization contract and security assumptions explicit so product teams can review changes alongside the operations they protect.