Permission systems have a well-known failure mode: start simple, get one feature request for a slightly-different-permission-shape, add a special case, repeat for years, end up with a matrix nobody fully understands and everybody's afraid to touch. We wanted to avoid that from day one, which meant deciding up front what we were and weren't going to support — before a single customer could ask us to make an exception.
So Attenzo has exactly four roles, strictly ranked: owner, admin, manager, employee. Every permission check in the product is a question of 'does this role rank at or above the minimum this action requires,' nothing more exotic than that.
What each role actually unlocks
- Employee — their own attendance, leave requests, and payslip visibility. Nothing about anyone else.
- Manager — everything an employee has, plus visibility into their team's attendance and the ability to approve or reject leave requests.
- Admin — everything a manager has, plus team management (roles, rates, shifts, device approval) and running payroll.
- Owner — everything an admin has, plus the only role that can grant or revoke owner-level access itself, and a guarantee that an organization can never be left with zero owners.
Why every check happens twice
A common and dangerous mistake is trusting the UI to be the permission boundary — hide the 'Payroll' link from anyone who isn't an admin, and call it done. That protects the page. It does nothing to protect the underlying API endpoint, which is reachable directly by anyone who knows (or guesses) the URL, completely independent of which page linked to it.
So in Attenzo, every single API route re-checks the caller's role itself, from the session, independent of whatever page happened to call it. The UI-level checks exist purely for user experience — showing the right navigation, redirecting someone who lands on a page they can't use — never as the actual security boundary. If we ever removed every role check from the UI entirely, the data would still be exactly as protected as it is today.
The tradeoff we accepted
Four fixed roles means we can't yet express 'this manager can approve leave but not see payroll data' or other custom combinations — that's a real, deliberate limitation, not an oversight. We'd rather ship a permission model simple enough that every engineer on the team can correctly reason about who can do what, than a fully generic custom-permissions system that's more flexible but structurally harder to keep provably correct as the product grows. If granular custom permissions become a real, common request, that's a foundation we can extend — but we didn't want to build it speculatively before anyone actually needed it.