Anatomy of a permissions system for owner, finance, and delivery staff
How a permissions system is actually built: workspace, branch, team, person, role, and device kept separate; capability grants instead of role-name checks; approval thresholds as a separate concern; and the failure modes to design against.
Almost every business we meet has the same administrative debt: one person set the system up, so that person can change everything, and everyone else has been sharing their login. It works until a resignation, a dispute, or a counter rush — at which point there is no way to tell who did what, and no way to remove one person without unlocking the whole business.
This is a walkthrough of how a permissions system for owner, finance, and delivery staff is put together: which things are separate objects, why capability grants beat role-name checks, why approval thresholds are a different problem from permissions, and the four failure modes we design against.

Six things people conflate into 'user roles'
The objects, and why each one is separate
| Object | What it answers | Why it is not the same as the one above |
|---|---|---|
| Workspace | Whose records are these? | The tenant boundary. Nothing crosses it implicitly. |
| Branch or store | Which physical place? | One workspace can hold several; reporting can consolidate without merging them. |
| Team | Which group of people works together? | A grouping for ownership and views, not a permission grant. |
| Person | Who is this human? | Carries identity, history, and deprovisioning. Lives on the person, not the device. |
| Role | What is this job? | A named bundle of capabilities. One role per job, never per person. |
| Device | What was used? | A shared till is a device many people use; it should never be the identity. |
The permission is meaningless until the scope is decided, which is why scope comes first. A capability that says 'may record a payment' only becomes enforceable once you know which workspace and which branch it applies in. The same person can hold different capabilities in two branches without holding two identities — that is the difference between scoping access and duplicating accounts.
Capability grants, not role-name checks
The weak pattern is a string comparison scattered through the code: if role === 'manager', allow this. It works until a business has a supervisor, a regional manager, and an owner-operator, and the code has grown a chain of special cases nobody can reason about. The strong pattern is a table of capabilities, and roles as named collections of them. The check asks whether this person holds this capability in this scope. Renaming a role then changes nothing about behaviour, and adding a supervisor becomes a data change rather than a release.
Our published split follows this shape, and we state it plainly rather than implying it. A verified example: member, admin, and owner may update a task and change task status, while creating a company, creating a quote, creating an invoice, recording a payment, and managing project members are denied to member and allowed to admin and owner. Delivery access covers reading client and project context, creating and updating tasks, and changing status — the work itself, not the money. Viewer access derives from what the workspace permits to be read, and stops there.
Where the counter split usually falls (illustrative planning matrix, not a per-customer configuration)
| Action | Owner | Manager | Cashier |
|---|---|---|---|
| Create a sale | Full | Full | Full |
| Apply a discount within limit | Full | Full | Limited |
| Approve an above-limit discount | Full | Full | None |
| Refund a sale or void a bill | Full | Full | None to limited |
| Manage products, prices, or stock | Full | Limited | None |
| Manage staff accounts | Full | None | None |
| Close the day or approve a variance | Full | Full | None |
| Export data or read the audit log | Full | None | None |
Threshold approvals are a different concern from permissions
Permissions answer 'may this person do this at all'. Thresholds answer 'may they do it this time, this large, in this situation'. Mixing them produces two bad outcomes. If a threshold is expressed as a permission, you end up with roles named 'discount-up-to-ten-percent', and you now have role explosion — a role per person, per limit, per product line. If a permission is expressed as a threshold, you end up with a rule engine that no longer describes who is accountable.
Keeping them apart is what makes a control reviewable. The cashier holds the capability to apply a discount up to the limit. Above it, the request does not fail silently and does not get waved through: it becomes a request with a value, a reason, a requester, and a named approver, and the decision is recorded. The exception path is the audit trail, not a gap in it.
What a threshold control needs to be reviewable
- The limit is explicit, per action, rather than implied by a role name
- The requester and the approver are separate, recorded fields
- A rejected request does not alter the underlying record
- The reason is factual rather than a preset dropdown someone clicks through
- Overrides are recorded and reviewable, not performed quietly
- The approver cannot be the person who raised the request
- Thresholds are reviewed when the business changes, not only at setup
PIN access for counter staff, and why the device is not the identity
Counter hardware belongs to the business. Requiring a full account and password on a till that four people share in a shift is how shared logins come back. PIN-based staff access ties each action to the person's own record instead of to the session on the device, which is what makes a shift have one owner and makes the shift record worth reading.
The operational caveat is the one people skip. A PIN on a sticker beside the till is not access control, and two staff sharing a PIN on the same shift reintroduces exactly the problem the PIN solved. PIN discipline is policy, not software, and it is worth setting the day you introduce it rather than the day you notice the problem.
The audit history is the actual product
Permissions restrict what can happen. Audit history is what lets you find out what happened. A permission system without a readable log is a speed bump; with one, it is a control, because the review is possible after the fact.
What the history needs to hold: who acted, in which workspace and branch, on which record, what changed, when, and under which capability or approval. It does not need to record every keystroke, and it should not carry customer data it does not need. Access to the log is itself a permission, and it is one of the few that should be genuinely narrow — whoever can read the audit trail can see the shape of the business's vulnerabilities.
Four failure modes worth designing against
How permissions systems go wrong in small businesses
Do instead: One role per job. A role whose description cannot be written in a sentence is a role to collapse. Review the list on a fixed cadence, not when something breaks.
Do instead: Grant capabilities to the role, assign people to roles, and treat a job change as an assignment change with a date. If access still matches last year's job description a year later, it is not access control.
Do instead: Give each person their own record, even if the credential is a PIN. Deprovision the person, not the device everybody shares.
Do instead: Enforce separation at the point of approval, not by convention. Self-approval is the exception most likely to be requested and the one most likely to be granted for convenience.
Where this model stops, honestly
The split described here is role-level. It decides which actions a role may perform and what the workspace allows to be read. It is not field-level masking: if you need a margin column hidden from one role and visible to another, that is a different feature and we do not claim it. Nor is it an identity provider — there is no single sign-on, no directory sync, and no password-policy engine for the rest of your stack. HR, payroll, and attendance are out of scope; a shift in our model exists to make counter activity attributable, not to run payroll. And it does not manage devices: what a device is gets recorded, what that device is allowed to do is your IT decision.
What we have not measured
Nothing in this article is a measured claim. We have not counted how many workspaces develop role sprawl, have not measured how often permission drift causes a financial error, and have no data on how shared logins correlate with variance at close. Those would need instrumentation we do not publish. What we can support is the structure and the reasoning, and the published permission matrix, which you can inspect rather than take on trust.
Frequently asked questions
Do counter staff need a full account and password?
Not necessarily. PIN-based staff access gives each person their own identity without the friction of a full credential, which is what makes a shift or a counter action attributable to a person rather than to a device or a shared session. The operational risk is a PIN treated as a shared secret, which reintroduces the problem it was meant to solve.
Should I configure roles or individual permissions?
Roles, made of named capabilities. Grant capabilities to a role and assign people to roles, so a job change is an assignment change with a date rather than a permissions rebuild. Role-name checks scattered through the product are what create role sprawl later.
Can I hide a specific field, like margin, from one role?
Not as a field-level rule. The published split is role-level: which actions a role may perform and what the workspace allows to be read. Column-level or cell-level visibility is not part of this model, so raise it during a walkthrough rather than assuming it is included.
What is the difference between a permission and an approval threshold?
A permission answers whether an action is allowed at all. A threshold answers whether it is allowed at this value. Above a discount limit, the capability still exists but the action becomes a recorded request with a reason and a separate approver, which is what makes the exception reviewable.
Can the person who requests an approval approve their own exception?
It should be blocked at the point of approval rather than discouraged by convention. Self-approval is the exception most likely to be granted for convenience and the one that removes the control entirely, so the separation belongs in the model.
What happens to their access when someone leaves?
Because identity lives on the person record rather than on the shared device, deprovisioning is a per-person action. Audit history remains readable afterwards, so what that person did while they were here stays available — which is the point of separating the person from the till.