NoLagZA is a competitive gaming platform: clans, memberships, invitations, match lobbies, readiness states, results and rankings. Almost every interesting bug in a system like this is an authority bug — someone doing something they should not be able to do, usually by accident, usually through a path nobody drew on the diagram.
Roles are not permissions
The first mistake is treating a role as a permission. "Captain" is not a permission; it is a label that implies a set of permissions inside a specific scope. A captain of clan A has no authority inside clan B. A moderator has authority over match results but not over clan membership. Once you write that down, the model stops being a list of roles and becomes a list of (actor, action, scope) triples.
Scope is the part people forget
- Global scope — platform moderation, result verification, account actions.
- Clan scope — invitations, membership changes, roster and clan settings.
- Match scope — readiness, lobby state, score submission, disputes.
- Self scope — profile, linked accounts, notification preferences.
Every authorisation check has to name both the action and the scope instance. "Can this user remove a member?" is unanswerable. "Can this user remove member X from clan Y?" is answerable.
Where authority leaks
Three patterns account for most leaks we have had to design around. The first is a check performed in the interface but not at the data layer — the button is hidden, the endpoint is not. The second is an implicit inheritance: a user is promoted, and an old membership row still grants an authority nobody revoked. The third is a state transition that skips validation, such as a match result written while the match is not in a state that accepts results.
If the only thing stopping an action is that the button is not rendered, the action is not prevented.
Practical rules we apply
- Enforce authority in the database, not only in the application.
- Keep authorisation separate from authentication — being signed in is not a permission.
- Model state machines explicitly, and reject transitions instead of tolerating them.
- Revoke on membership change, never rely on a later cleanup pass.
- Test each endpoint with at least three actors: the owner, a different member, and a stranger.
None of this is exotic. It is ordinary engineering discipline applied consistently, which is what separates a platform that survives a competitive community from one that gets exploited in its first week.
