Skip to content

Roles & Permissions ​

This is the security baseline for Registry. Every feature page references it, and no feature may be defined without stating what each role can do with it. It describes the authentication boundary, the two independent permission planes, the roles on each plane, and the rules that shape access over time.

Source of truth. Roles and their permissions are stored as data in the database and loaded into memory when the backend starts. The seed migrations (V1_0_1, V1_1_1, V1_2_1, …) are authoritative; the matrices below reflect that seed data in business terms.

Authentication model ​

Registry is protected / private. Authentication is delegated to an external OIDC identity provider — there is no built-in password store. Practically:

  • Every screen and every API call requires a signed-in user, except the handful of authentication endpoints themselves (obtaining a login/logout URL, exchanging an authorization code for tokens, and refreshing tokens). API documentation and health endpoints are public only when explicitly enabled for an environment.
  • A user's identity comes from the provider. On first sign-in, Registry provisions the account automatically with the default global role and links it to the provider identity.
  • A blocked account (globally disabled) is refused at sign-in, and an anonymized account can never sign in again (see Users).

Two permission planes ​

Authorization is split into two planes that combine on every request.

PlaneQuestion it answersExamples
GlobalWhat may this user do across the whole platform, independent of any event?Manage user accounts; create a project.
Project-scopedWhat may this user do inside this specific event?Record a movement in project X; delete a group in project X.

A project-scoped permission is always bound to one project. Holding a permission in one event grants nothing in another — this is what makes Registry multi-tenant. Under the hood, a project permission is the pair (project, permission); the technical documentation explains how that is enforced.

Global roles ​

RoleLevelWho holds itPurpose
USER_ADMINISTRATOR0Platform staffAdminister every user account; also allowed to read/create projects and grant support access.
USER9000Everyone, by defaultThe role each new account receives. Can create projects and read project metadata. Nothing else at the global level.
SERVICE_ACCOUNT—The system itselfA user type (not a role of its own) for the single non-human account that runs the scheduled data-retention jobs. It is provisioned with the USER_ADMINISTRATOR role — the role that carries REGISTRY_JOB_C — and is not assignable to people.

Lower level means more powerful. Exactly one level-0 global role exists.

Global access matrix ​

CapabilityUSER_ADMINISTRATORUSER
View the user directory & user metadata (roles)✓—
Change a user's global role✓—
Block / unblock a user✓—
Anonymize (purge) another user✓—
Create a project✓✓
Read any project globally✓—
Read project metadata (available options)✓✓
Grant "support" access to a project✓—
Run data-retention purge jobs (REGISTRY_JOB_C)✓—
Anonymize their own account✓✓

The important onboarding consequence: any signed-in user can create a project, and the creator automatically becomes its PROJECT_ADMINISTRATOR. That is how ordinary users get project-scoped power without a platform administrator being involved.

Project roles ​

Assigned per project when a user is invited (or when they create the project). Every project-scoped resource is governed by these three roles.

RoleLevelWhat it is for
PROJECT_ADMINISTRATOR0Full control of one event, including its membership. The creator gets this automatically.
PROJECT_COORDINATOR10Runs the event's operations end to end, but cannot manage membership or delete the event.
PROJECT_PARTICIPANT20Ground-level staff: register people and record movements, and correct their own entries — but never delete, and no standing access to vehicles or activities.

Project access matrix ​

Actions use CRUD shorthand — Create, Read, Update, Delete. A dash means no access. Rows marked (option) only apply when the project has that module enabled (see Project options).

ResourcePROJECT_ADMINISTRATORPROJECT_COORDINATORPROJECT_PARTICIPANT
Project settings (name, dates, options, enable/disable)R U DRR
Members / profiles (invite, edit, block, remove)C R U DR—
ParticipantsC R U DC R UC R U
GroupsC R U DC R UC R U
Movements (check-in / check-out)C R U DC R U D ²C R U
Vehicles (option)C R U DC R U— ¹
Activities (option)C R U DC R U— ¹
Communications (option)C R U DC R UC R U
Alerts (option)C R U DC R UC R U
Live presence dashboardRRR

History is a separate capability — the ability to view a resource's own movement history — and is listed on its own row below rather than folded into a resource's CRUD, for readability. It is always read-only: history is a derived view of movements already recorded, not an editable resource in its own right, so there is no create/update/delete operation on it for any role.

History ofPROJECT_ADMINISTRATORPROJECT_COORDINATORPROJECT_PARTICIPANT
Participant movementsRR—
Vehicle movements (option)RR—
Activity movements (option)RR—

¹ A participant has no direct read access to the vehicle or activity registries, but may still select an eligible vehicle or activity while recording a movement, via the movement's own scoped search — see Movements and Technical → API Reference.

² Movements are the one resource where the coordinator keeps full Delete rights alongside the administrator — everywhere else, only the administrator can delete.

Reading between the lines — and this is less tidy than a first read suggests, so take the table as the source of truth over any summary of it:

  • Only the administrator manages membership. Coordinators can see who is in the event but cannot invite, edit, block or remove members. Participants cannot see the member list at all.
  • Only the administrator changes the project itself. Coordinators are read-only on project settings — they cannot rename it, change its dates or options, enable/disable it, or delete it.
  • Delete is the administrator's alone almost everywhere. For participants, groups, vehicles, activities, communications and alerts, only the administrator can permanently delete — the coordinator has full create/read/update (including disable/enable and, for alerts, status changes) but never delete. Movements are the single exception: the coordinator keeps delete there too.
  • Coordinators and participants are closer than the role names suggest. Below the administrator, both roles share the same create/read/update floor on participants, groups, movements, communications and alerts — participants are not limited to create-and-read. What actually separates a coordinator from a participant is: visibility into membership (coordinator only), movement history (coordinator only), any access at all to vehicles or activities (coordinator only, beyond what a participant can select inside a movement), and delete on movements (coordinator only, alongside the administrator).
  • Vehicles and activities are the coordinator's private territory. A participant has no standing access to either — not even read — beyond selecting an eligible one while recording a movement.

Project options (gating) ​

Four optional modules are enabled per project. A module that is off is invisible and its API is closed — regardless of a user's role. Turning a module off never deletes data: rows already created, and references already recorded inside movements (a vehicle assignment, an activity used as a reason), are kept in the database — they simply stop being reachable through the module's endpoints and drop out of the UI until the module is re-enabled. Modules also have dependencies:

OptionAddsRequires
VEHICLETrack vehicles and their presence; assign them in movements—
ACTIVITYPlan activities with capacity limits; use them as a movement reason—
COMMUNICATIONAttach messages to movements and alertsACTIVITY
ALERTRaise and manage incidentsACTIVITY + COMMUNICATION

So enabling alerts implies enabling communications and activities. The core — projects, profiles, participants, groups, movements and the dashboard — is always present.

Rules that shape access over time ​

  • Profile lifecycle. A membership (profile) moves through INVITED → ACCEPTED or REJECTED, and can be BLOCKED. Only an ACCEPTED profile grants any project permission.
  • Access window is optional. Each profile may carry a start/end access window. Outside that window the profile is unavailable and grants nothing, even if accepted. A profile with no window is permanent — it never expires on its own.
  • Visibility gating (disabled events). When a project is disabled (made invisible), non-administrators lose all authority on it. The administrator keeps only the ability to read, re-enable or delete the project — everything inside stays locked until it is re-enabled.
  • Role-level safety. A user may only assign roles at or below their own level, and the system refuses to remove or demote the last permanent level-0 administrator — the last one with no end date — of a project or of the platform. A temporary administrator (one with an end date, e.g. a support profile) never counts toward this safeguard, so it can never be used to strand an event with only expiring administrators. You can never orphan an event or lock everyone out of it for good.
  • Support access. A platform administrator can mint a temporary, one-hour administrator profile on any project ("support" / assistance profile) to intervene without permanently joining it. Being temporary, it never blocks — and is never protected by — the last-permanent-administrator safeguard above.

Adding a new role (reciprocity rule) ​

If a new role is ever introduced — on either plane — it must be defined against every existing feature before it ships. Concretely: add a column to the global or project matrix here, and add a row for the new role to the Roles & Permissions table on each page under Features. A role with an undefined permission for any feature is not a valid role.