Skip to content

Access

An account’s role and grants decide which entities it can read and which actions it can perform. Roles are documents an administrator edits; grants apply to every entity or to a scope (a kind, a tag, a site, a set of ids). Everything below lives in Terminal → Settings… (⌘,) under Access: Account for every signed-in user, and Users, Roles, API keys, Audit and Security for administrators.

The card asks for an email and a password. Three things can follow:

Mainframe answers The card shows
A second factor is enrolled A six-digit code. Use a backup code swaps in a text field; Trust this device for 30 days skips the code on this browser until then
The password was set by an administrator A password change: the current (temporary) password, the new one twice. The console opens once it is changed
The security settings require a second factor this account has not enrolled The enrolment dialog, with sign-out as the only other way out
The account is disabled Account disabled

Sign in with a passkey appears when passkeys are enabled in Security; Continue with <label> when single sign-on is. A wrong password reads Wrong email or password; too many attempts in the configured window read Too many attempts.

Row Does
Name Editable; Save
Email, Role Read-only; an administrator changes them
Password Change…: current, new, confirm. Other sessions end; this one continues
Two-factor Enable…: the password, then a QR code and the secret for an authenticator app, then the six-digit check, then ten backup codes shown once with Copy. Disable… asks the password
Passkeys Listed when enabled in Security; Add passkey registers this device’s authenticator
Sessions Every open session with this device marked; Revoke per row, Revoke other sessions
Personal API keys Keys that act as you for scripts (Authorization: Bearer mf_user_…); New key names one and sets its expiry (the Security maximum applies); the value is shown once

The table lists every member: name, email, role (a select that applies at once), whether a second factor is enrolled, join time and original invite code (when present), the last sign-in, and the state (active, disabled, must change password). New user creates an account with a temporary password shown once, or with a password you type; the user changes it at the first sign-in. The row menu offers Reset password… (the same choice), Sessions… (revoke one or all), Grants… (a table of action × scope beyond the role), Disable / Enable, and Delete user… (typed confirmation of the email). Disabling, deleting, a role change, a grant change and a password reset end the user’s open connections at once; the last administrator cannot be demoted, disabled or deleted, and you cannot disable or delete yourself.

Open Users → Invites to create reusable, role-bound links. Creation defaults to field and Unlimited uses; choose a role and optionally set a whole-number limit. Administrator roles require an additional confirmation. Copy the link, show its QR code, or download SVG/PNG for printing or sharing. Anyone holding the link can register with that role while the invite is available.

The table shows code, label, role, successful uses, status, creator and creation time. Edit Limit… changes its label or cap; role is fixed. Retire Invite… permanently stops new registrations. Existing users keep access, deleted users do not return uses, and failed or repeated registration requests do not consume extra uses. Raising an exhausted invite’s cap makes it available again.

View Users filters the users table to that invite. Joined and Invite record membership creation and its source; they remain after retirement or role changes. Both visible views refresh periodically and when the window regains focus, so background API registrations appear without reopening Settings.

The public link displays workspace, role and availability. Clients register through the invite API with a name, email-format identifier and password, then use ordinary sign-in. Synthetic email addresses are accepted; username authentication is not added.

Cards for every role with its member and grant counts. admin, operator, field and observer are built in (field is for people on the ground with Front: it sees everything an observer sees, talks and chats, marks and discusses the map, and captures, but commands nothing): their grants are read-only, Duplicate… starts a custom role from one. New role and Edit… open the editor: name, description and the grants table. A role held by members or referenced by a non-retired invite (including an exhausted invite) cannot be removed. A role edit applies to its members at once (their connections restart).

Module keys authenticate modules (weather, TAK, the simulator…). A key created at boot from the environment carries from env. Rotate creates a second enabled key for the same module, shown once: restart the module with the new value, then Revoke… the old one. Personal keys are yours, as in Account.

auth.* events (sign-ins with their outcome and address, sign-outs, second-factor and password changes), access.* events (every change above, with who did it) and settings.* events (every settings save with its diff). Filter by kind, by principal (a name or an id) and by range; Details shows the event’s data; Export CSV writes the loaded rows (at most 1000).

The security settings document: session age, the login rate limit, the password minimum length, the two-factor requirement (Off, Admins, Everyone; the row warns when no administrator has enrolled), passkeys, single sign-on with its button label, and the personal key maximum age. Rows marked after restart apply at the next Mainframe start. Single sign-on also needs MAINFRAME_SSO_ISSUER, MAINFRAME_SSO_CLIENT_ID and MAINFRAME_SSO_CLIENT_SECRET in the environment; the row says whether they are set. A user the identity provider creates joins as an observer.

Actions are entity.read, entity.telemetry.read, entity.video.read, entity.control, entity.manage, entity.command.<capability>, history.read, replay, docs.<collection>.read|write, system.admin, *, and the four comms actions chat.read, chat.write, voice.listen and voice.talk. A scope is all, a kind, a tag, a site or a list of ids. Kinds and tags are the ones providers announce, so a tag-scoped grant is as trustworthy as the module that tags the entity. Grants from mainframe.config.ts show as config and are synced at every boot; grants written here survive restarts.

Chat groups and voice rooms are permissioned the same way, and the grant is the membership: nobody joins or leaves a group. A group is matched as its id with kind chat-group and the tags set in Settings → Groups; a room as its id with kind voice-room. So chat.write on all writes in every group, on kind chat-group likewise, on a tag follows the groups carrying it, and on ids names the groups; the ids picker lists them as group · Ops and room · Team Alfa beside the entities, and the kind picker offers chat-group and voice-room. The built-in operator and field roles hold all four actions on all; the observer reads chat and hears rooms; both read the group and room lists. A site scope never matches a group or a room. For voice, voice.listen joins a room and hears it and voice.talk publishes the microphone in it: the join token Mainframe mints carries exactly those two answers, and a role or grant change removes the user from every room at once so the next join reflects it (see Voice).