Users, roles & organizations.
Organizations & quotas
What it is. Multi-tenancy. A single AccessFlow deployment hosts one or more organizations, each a fully-isolated tenant — every user, datasource, query, and audit row belongs to exactly one org, and a tenant can never reach another's data. Reach for it when you host several teams or customers on one deployment and want per-tenant resource caps; most operators run a single org and can ignore the cross-org controls.
Platform admin (super-admin). Managing organizations across the cluster
requires the platform-admin capability — a flag on a user, separate from the four
roles below. A platform admin keeps their normal org role and additionally sees a
Platform navigation group with the /admin/organizations
screen. The first admin you create (via the setup wizard or the
bootstrap env vars) is provisioned as a platform admin
automatically.
/admin/organizations — platform admins manage every tenant and its quotas from one screen.Configure it. Platform admins manage tenants and their quotas from
/admin/organizations:
- Create an organization. From
/admin/organizations, click New organization, give it a name (an optional URL-safe slug is derived from the name when blank), and optionally set quotas. The org starts enabled and empty — add its first admin and datasources afterwards. - Set per-org quotas. Cap how much a tenant may consume:
max datasources, max users, and max queries per day (a rolling
trailing-24-hour count). Leave a field blank or set it to
0for unlimited. Quotas are enforced when the resource is created — a breach is rejected with HTTP409 QUOTA_EXCEEDEDand a message naming the limit, so the datasource / user / query simply isn't created. - Edit name & quotas. Open
/admin/organizations/<id>to change the name or raise / lower a quota, and to see live usage bars (current count vs. limit) for each cap. - Disable a tenant. Use Disable as a kill-switch — its users are blocked at login (local and SSO) and every in-flight session stops on its next request. The block is immediate (no cache, no waiting for token expiry). Enable restores access.
Every organization action (create, update, disable, enable) is written to the audit log against the target org. Per-org login pages / SSO routing across multiple orgs are not yet available — see the roadmap.
Languages & localization
What it is. AccessFlow ships seven bundled interface languages — English, Español, Deutsch, Français, 简体中文, Русский, and Հայերեն. An org admin decides which of them users may choose, which one users start on, and which language the AI reviewer writes in.
Configure it. /admin/languages (admin-only) has three
controls: an available languages checklist (at least one required) that drives
the language switcher in the top bar; a default language select, narrowed to that
allow-list, applied to users who have not picked one; and an AI review language
select spanning all seven languages independently of the user allow-list. Saving
refreshes the top-bar switcher immediately.
Tune it. Users override the default themselves from the language switcher next to the theme toggle, and the choice persists to their profile. Any string missing from a translation falls back to English.
Users
What it is. The people who can sign in to AccessFlow and the role each one carries. Create accounts directly, or let SAML / OAuth users be auto-provisioned on first sign-in when SSO is enabled (see SAML and OAuth).
Configure it. Manage everyone from /admin/users:
/admin/users → Invite via email. Enter the recipient's email, pick a role, and AccessFlow emails a one-time signup link.- Invite via email (default). From
/admin/users, click Invite via email, fill in the recipient's email, optional display name, and role, then submit. AccessFlow generates a signup token and emails it; the link expires afterACCESSFLOW_SECURITY_INVITATION_TTL(default 7 days). - Create with a password. Use the dropdown next to the invite button → Create with password to provision a user directly. Useful when SMTP isn't configured yet, or when you want to seed an account synchronously.
- Edit or deactivate. Click any row to change the role or flip the active toggle. Deactivated users can't sign in but their audit trail is preserved.
- Pending invitations are listed below the user table; resend or revoke them from there.
Tune it. ACCESSFLOW_SECURITY_INVITATION_TTL (invite-link
lifetime, default P7D), ACCESSFLOW_SECURITY_PASSWORD_RESET_TTL
(reset-link lifetime, default PT1H), and
ACCESSFLOW_SECURITY_PASSWORD_RESET_RESET_BASE_URL (link base, default
http://localhost:5173).
User roles & RBAC
What it is. Role-based access control. Every user carries one org-wide role that caps what they can do; on top of it, per-datasource permissions decide which databases they may touch. Pick the lowest-privilege role that still lets someone do their job — and a user can never approve their own query, whatever their role.
Custom roles. Beyond the five built-in system roles, an admin can compose
custom roles on /admin/roles from a fixed catalog of functional
permissions (submit SELECT/DML/DDL, review queries, review access requests, manage
datasources, view the audit log, and so on) — e.g. a reviewer who may approve queries but
not manage users. The five system roles are immutable and behave exactly as the matrix
below; a custom role grants exactly the permissions you tick. Roles that are still
assigned to users cannot be deleted.
Configure it. Assign a system or custom role when you create or edit a
user on /admin/users; the matrix below is what each built-in role may do.
Platform admin is separate from the four roles. The
platform-admin capability is an orthogonal flag, not a
fifth role — a platform admin keeps whatever role their home org assigns and is
additionally allowed to manage organizations across the cluster
(/admin/organizations). It grants no extra capability inside any single org;
the matrix below still governs every tenant-scoped action.
| Capability | READONLY |
ANALYST |
REVIEWER |
ADMIN |
AUDITOR |
|---|---|---|---|---|---|
Submit SELECT queries | ✓ | ✓ | ✓ | ✓ | — |
Submit DML (INSERT / UPDATE / DELETE) | — | ✓ | ✓ | ✓ | — |
Submit DDL (CREATE / ALTER / DROP) | — | — | — | ✓ | — |
| View own query history | ✓ | ✓ | ✓ | ✓ | — |
| View all queries in the org | — | — | ✓ | ✓ | — |
| Approve / reject queries | — | — | ✓ | ✓ | — |
| Request time-bound datasource / API-connection access (JIT) | ✓ | ✓ | ✓ | ✓ | — |
| Review / approve access requests | — | — | ✓ | ✓ | — |
| Manage datasources | — | — | — | ✓ | — |
| Manage users | — | — | — | ✓ | — |
| Manage user groups | — | — | — | ✓ | — |
| Manage review plans | — | — | — | ✓ | — |
| View audit log | — | — | — | ✓ | — |
| Manage notification channels | — | — | — | ✓ | — |
| Configure AI | — | — | — | ✓ | — |
| Configure SAML / OAuth | — | — | — | ✓ | — |
| View / export compliance reports | — | — | — | ✓ | ✓ |
| View behavioural anomalies (UBA) | — | — | — | ✓ | ✓ |
| Acknowledge / dismiss anomalies | — | — | — | ✓ | — |
| Break-glass / emergency execution† | ✓ | ✓ | ✓ | ✓ | — |
| View break-glass log | — | — | — | ✓ | ✓ |
| Acknowledge break-glass events | — | — | — | ✓ | — |
† Break-glass / emergency execution is not granted by role — it is gated by a
separate per-user, per-datasource can_break_glass permission that an admin
grants explicitly (required for everyone, including admins; time-boxed). A user can
break glass only on a datasource they hold that grant for, and only for query types they
already have the capability for.
Which role for what. Use READONLY for people who only
need to look at production data (analysts, on-call engineers reading dashboards).
Use ANALYST for people who write data through reviewed queries.
Use REVIEWER for people who approve other users' queries — typically
senior engineers or DBAs. Use ADMIN for the platform-team operators who
configure the system itself. Use AUDITOR for a dedicated, read-only
compliance reviewer — it sees only the compliance dashboard (/admin/auditor):
pre-built PII/PCI/GDPR access and DDL/DELETE reports with signed PDF/CSV export, and
nothing else.
Datasource-level permissions. Role is the org-wide ceiling. On top of
it, every user needs an explicit per-datasource permission grant to
access a given database — it controls read / write / DDL per
datasource, row caps, allowed schemas / tables, and restricted columns (which are masked
as *** in SELECT results). See
docs/07-security.md
for the full authorization matrix.
Group-based access grants. Rather than a row per person, an admin can grant a user group access to a datasource or an API connector (same read / write / DDL / break-glass controls); every member inherits the grant, and adding someone to the group gives them access without a new grant. When a user has both a direct grant and one or more group grants, their effective access is the most-permissive union — capabilities are OR-ed, allow-lists merge, restricted-column masks apply only where every grant restricts, and each grant's expiry is honoured independently.
Just-in-time (JIT) access requests
Instead of an admin pre-granting a
permission, any user can request temporary, scoped access from
/access-requests — to a datasource (pick the capabilities they
need — read / write / DDL — and an optional schema/table scope) or to an API
connection (read / write plus an optional allow-list of specific operations from
the connector's schema catalog), with a duration. The request runs
through the same reviewer-eligibility and multi-stage approval engine as query review
(a requester can never approve their own); API-connection requests route through the
connector's assigned review plan. Admins are the backstop approver: an admin
sees and can approve every pending access request from
/admin/access-requests — even on resources with no review plan — so a
request is never stuck waiting for an approver who was never configured. On final
approval AccessFlow writes a time-boxed permission grant (expiring at
now + duration) — a datasource permission, or an API-connection permission
visible on the connector's Permissions tab alongside admin-granted rows; it's revoked
automatically on expiry, and an admin can revoke an
active grant early from /admin/access-requests. Tune the revocation cadence
with ACCESSFLOW_ACCESS_GRANT_EXPIRY_POLL_INTERVAL (default PT5M)
and the allowed duration window with ACCESSFLOW_ACCESS_MIN_DURATION /
ACCESSFLOW_ACCESS_MAX_DURATION (defaults PT15M / P30D).
A requester can additionally tick “Pre-approve queries under this grant” on the
request form (off by default): while such a grant is active, queries it covers —
matching capability and schema/table scope — skip human review entirely and are
auto-approved with the grant and its approver recorded on the query detail and in the
audit log. The flag is shown as a highlighted tag in the approval queue so the reviewer
sees exactly what they authorize; auto-reject and escalation routing policies, high-risk
AI verdicts, and open behavioural anomalies still override the fast-path.
/admin/access-requests — pending JIT access requests; admins approve, reject, or revoke an active grant.Break-glass / emergency access
For genuine emergencies — production is
down and approvers are unreachable — an admin can grant a user the
can_break_glass permission on a datasource (a checkbox on the permission
grant, alongside read / write / DDL, time-boxed via the same expires_at).
With that grant, an Emergency access button appears on the editor for
that datasource: the user supplies a mandatory justification and the query
executes immediately, bypassing review — but still through every proxy
guard (schema/table allow-list, dynamic masking, row-level security, row caps). The grant
is required for everyone, including admins. Each break-glass execution fires
instant notifications to all admins (including PagerDuty), writes a prominently-tagged
QUERY_BREAK_GLASS_EXECUTED audit row, and opens a mandatory
retro-review on the /admin/break-glass log that an admin —
never the submitter — must acknowledge after the fact. The executed query keeps
its normal terminal state; the retro-review is tracked alongside it.
/admin/break-glass — every emergency execution opens a mandatory retro-review here for an admin (never the submitter) to acknowledge.User groups
What it is. Named, organisation-scoped collections of users. Use them to (1) bundle reviewers so you can attach a single group — instead of ten individual users — to a datasource as eligible reviewers, (2) grant a whole team data or API access (a datasource or API-connector grant on a group is inherited by every member, so you don't add a row per person), and (3) act as the target of IdP group mappings so SAML / OAuth2 logins keep membership in sync automatically.
Configure it. Manage groups from /admin/groups:
- Create a group. Go to
/admin/groups→ Create group. Pick a name (e.g. Billing Reviewers) and an optional description. - Add members. Open the group, click Add member, and pick users from the dropdown. Manually-added members are tagged Manual and stay put regardless of the IdP sync.
- Use the group. On a datasource's Reviewers tab
(
/datasources/<id>/settings), add the group as a reviewer. From that point on, members of the group can see and decide queries against that datasource (in addition to plan-approver rules). On the same page's Permissions tab (and an API connector's Permissions tab) you can also grant the group access — switch the grant target from User to Group and every member inherits the read / write / DDL / break-glass grant. - Optional: IdP-managed memberships. Configure
group_mappingson the SAML or OAuth2 admin pages so an IdP group claim auto-maps to the AccessFlow group. On every login, AccessFlow replaces the user's IdP-sourced memberships with the mapped set; Manual memberships are never touched.
Per-datasource reviewer scoping. Once a datasource has at least one assigned reviewer (a user or a group), only those reviewers see its queries. Datasources with none fall back to the review-plan approvers — so adopting groups is purely additive, no migration required.
/admin/groups — organisation-scoped user groups; open one to manage members.