Users and permissions
Shipped
Every Turbobase instance has its own user directory and its own permission model. Who can sign in, what they can do, and which rows they can see are all decided inside your instance — not borrowed from a source system.
A role is a named set of permissions. Permissions are not edited person by person: you author roles, then assign them.
Three roles are built in and ship with every instance:
- Admin — everything, including settings, sync, the Analyzer and user management. Administrators bypass capability checks entirely, manage other users, and mint access tokens.
- Data admin — full read, write and delete on every record type, including record types added later. No admin functions.
- Standard — the default grant for an invited user: read and write across the order-to-cash, procurement and work-management records, read-only on the GL and HR surface.
Built-in roles cannot be edited or deleted. To start from one, use Copy to a new role — you get an editable role with the same permissions. (Admin is the exception and cannot be copied: it bypasses capability checks, so a copy would either be a second unrestricted administrator or a standard role that only looked like one.)
The one exception is session length, below: a built-in carries its own, set from a card on its otherwise read-only page. Nothing it grants can be changed.
Custom roles
Section titled “Custom roles”Administrators author their own roles at /admin/roles — a warehouse clerk, an AP specialist, a read-only auditor. The role editor has four sections:
- Record permissions — the records this role may work with. Add a record, then tick what the role may do with it. The list starts empty and holds only the records you added, so a role never grants more than you chose; Add record offers every record type your instance carries — printers and print jobs as much as sales orders — and your own custom tables beside them, marked with the source they came from. Removing a record revokes every verb it held. Record types added later inherit nothing unless you grant a wildcard.
- Action capabilities — permissions that are not tied to a record type.
- Record restrictions — limit which records the role can see by field value: pick a field, list the allowed values. A record with an empty value on that field is always visible. Use
{{user.<field>}}(for example{{user.id}}) to match the signed-in user’s own value instead of a fixed list, so “only this user’s own records” follows whoever is signed in. - Content access — the Slack channels whose message history the role may read, one channel at a time, chosen from the public and private channels your instance mirrors. Holders read a granted channel’s history in Turbobase even if they are not members of it in Slack, or not Slack users at all. Granting nothing reads nothing — the opposite of a restriction, where an empty value means unrestricted. Tick Post into these channels to let holders post into the granted channels from an app as well, member or not — and nothing else: no other channel, no edits, no other activity. The message goes out as your workspace’s Slack app, and holders still can’t edit or delete messages in a private channel they aren’t in. Pilot still follows Slack membership, and direct messages can never be granted. The role also needs Read granted message history, which the editor ticks for you with the first channel; at most 16 channels per role. Adding a channel your instance hasn’t mirrored as a public or private channel is refused when you save, and the message names the channel — a grant like that would read nothing and post nothing. A channel already on the role that has since left your workspace keeps saving, so you can still rename or re-permission that role and remove the channel when you’re ready.
Saving a role re-issues permissions for everyone holding it on their next sign-in, and the page tells you how many holders that is. Removing a channel from Content access takes effect sooner: a holder loses that channel’s history on their very next request. A role with holders can’t be deleted. Role creation and edits are written to the audit log, and the whole grant as it stood before and after the edit is on the audit row’s own before/after columns — so Show diff on that row reconstructs a capability you removed from a role thirty people hold, without needing a backup.
A user holds a set of roles
Section titled “A user holds a set of roles”A user may hold several roles and switch between them; exactly one is their default. The default is the role a fresh sign-in mints. A user switches freely inside their own set but cannot re-point their own default — that’s an administrator’s call. Switching reloads the app under the new role.
A user can’t be left holding no roles. To take away their named grant entirely, detach them from roles instead; their permissions then come from their own stored bundle.
Users provisioned from a connected source are managed by that source — Turbobase roles do not apply to them.
How long a role’s people stay signed in
Section titled “How long a role’s people stay signed in”The role editor carries one more field beside the name and description: Keep this role’s users signed in for, in whole days. Leave it empty and the role’s people get the instance-wide default from Settings → Sign-in → Sessions. Fill it in and that value wins for them instead — in both directions, so a role may be longer than the default or shorter than it. The limits are 1 day and 30 days, and the editor refuses a fraction or anything outside them before it saves.
Built-in roles carry it too, from a card of their own on the read-only page. It is the only thing about a built-in you can change.
When a user holds several roles, the one they are acting as decides. A fresh sign-in mints their default role’s length; switching role re-issues the session with the new role’s length, so a person who moves from a 1-day role to a 30-day one gets 30 days from the moment they switch and not before.
Shortening a role reaches the people already holding it — their next session renewal is minted at the shorter length, without anybody having to sign out. See Authentication levels for how the instance-wide default is set and what never follows it.
Editing a role interrupts the people holding it
Section titled “Editing a role interrupts the people holding it”Changing what a role may do reaches its holders’ records immediately, through every client. It used to reach them only when their session next renewed, which could be up to half their session length away — fifteen days on a 30-day role — so somebody moved to a narrower role went on reading what the wider one allowed. Their data is now refused the moment you save.
What a holder sees depends on where they are:
- In an app — reads and writes start being refused, and a well-built app sends them to the sign-in page, which hands them a fresh session carrying their new access without asking for a password.
- In the console — their next page asks them to sign in again. They are not locked out and nothing is lost; signing in gives them the role you just saved.
So edit a role when an interruption is acceptable, the same way you would time any other change that touches who can see what. Adding a capability interrupts in exactly the same way as removing one — the instance does not try to tell a widening apart from a narrowing, because the holder’s session no longer matches their access either way.
Capabilities and scopes
Section titled “Capabilities and scopes”Permissions are enforced in the database by row-level security, so they hold no matter how a request arrives — the app, the PostgREST API, or the MCP server. Two axes combine, and one grant runs the other way:
- Capabilities — a verb × record-type vocabulary (for example
read:sales_order,write:invoice,read:entity:customer) that gates which operations a role may perform on which record types. A write grant does not imply delete. - Scopes — the dimensional restrictions authored as Record restrictions above (for example by subsidiary or location) that limit which rows the role can see and touch. An empty scope on a dimension means unrestricted on that dimension.
- Content grants — the channels listed under Content access above. They work the other way round: message history is readable only for a channel the role names exactly, and only while the user is acting as that role. A person holding several roles reads a granted channel only while acting as the role that grants it.
Roles an app declares
Section titled “Roles an app declares”An app can declare the roles its screens need, and deploying it creates them here as ordinary roles — with no holders until you assign them. An app may also declare a role’s restrictions and content access. Those values are marked declared by the app in the editor, and your edits win: a channel you remove is not re-added by the app’s deploys for as long as the app keeps declaring it, and a restriction you change keeps your value — the editor says so beside it. The same holds for a capability you remove from a role an app declared: the app’s deploys never add it back — nor a wildcard that would grant it — and report it as kept removed until you grant it again here. A channel you add is never removed by the app — even if the app later declares the same channel, because an app removes only what its own deploys put on the role. The same holds for a restriction you set: if an app later declares exactly that value, it shows declared by the app, but the app can never clear or change it. An app that stops declaring a channel it added removes it on its next deploy, unless another app on the same role still declares it.
Access is invite-only
Section titled “Access is invite-only”A person can sign in to your instance only if they’ve been invited. An authenticated identity that was never invited gets no session — there is no open self-signup into an instance. Invited users are matched on their verified email, and suspended users are refused on every request.
Suspending somebody reaches their data, not just their next page. Their reads and writes of your instance’s records stop at once, from every client — the console, an app, the API and the live data stream alike — whether or not they have a browser tab open and however long their session had left to run. Reactivating them restores it just as directly: they sign in again and carry on. The same holds when somebody signs out: the session they signed out of stops working there and then, and their other sessions on other devices are left alone.
Somebody you invite holds the role you gave them from their first sign-in — its permissions, and nothing else. If you picked a custom role on the invite, that is the role their very first session is minted under; you do not have to assign it again once they arrive. The same holds for somebody who joins through a chat invite link — Telegram, Slack or Discord — who lands on the built-in role the invite named.
Repeated attempts are slowed down
Section titled “Repeated attempts are slowed down”Sign-in and password-reset attempts are limited, so nobody can sit and guess at a password, a one-time code or a reset code. Two limits apply at once: one on how fast any single network address may try, and one on how many attempts may be made against one account in ten minutes — so trying the same account from many machines is no faster than trying it from one.
Somebody who trips a limit sees “Too many attempts. Wait a moment and try again.” on whichever screen they were on. Nothing is locked and nobody has to be let back in: waiting clears it, and the person signs in as normal.
Asking twice in a row for a reset email sends one email, not two. The screen says the same thing either way, and the second request is still recorded in the audit log — only the duplicate email is held back, so a fresh link cannot be used to bury the one already sitting in somebody’s inbox.
Managing users
Section titled “Managing users”Administrators manage users at /admin/users — inviting people, assigning roles, and suspending or reactivating accounts. Each identity in the list carries its own profile picture beside it, or the person outline where nobody has set one; a picture is set by the person themselves, under Profile on their own account page. Every such change is written to the audit log, with the person’s access before and after the change on the row’s own before/after columns.
A password change and a password reset are two different events, and they filter separately. A change is made by someone already signed in who knows the old password; a reset is made through the emailed link, by someone who may have lost control of the account. Reading them as one event hides exactly the case worth looking at.
View as a user
Section titled “View as a user”“The Warehouse role can’t see fulfilments” is a question you can answer by looking, rather than by reading the role editor and guessing. From a user’s page, View as this user opens a read-only session that reproduces exactly what that user sees. It lasts 10 minutes, cannot write anything, and every action is still attributed to you; your own session resumes when you exit from the banner. Inside it, every button, form and menu item that would change something is shown disabled and says why — the session is read-only — so what you see is what the session can do, not a control that fails only when clicked.
Because a user holds a set of roles, the control sits on each row of their assigned-roles table — you look through one role at a time, and the banner names which. An administrator may view any user, but only under a role that user actually holds.
Two cases refuse: a suspended user (a session for them would be the one way to get a live one while they are suspended), and starting a second view-as from inside one — view-as sessions do not chain. Exit the first from the banner.
Related
Section titled “Related”- Auth levels — how sessions and capabilities are minted
- Audit log — where role, user and view-as activity is recorded
- Data model — the RLS-protected tables these permissions gate