Admin Console¶
Availability: all tiers ยท Admin-scoped
See the Tier & Scope Matrix for the full breakdown.
Settings > Admin is the identity and access management (IAM) surface for the platform - Users, Organizations, API Keys, and Service Accounts. It's visible only to users whose role is admin.
Access control¶
The console checks your role client-side and shows an "Access denied - admin role required" panel if you're not an admin. That check is a UX convenience only - every mutation on these pages is independently re-validated server-side by require_admin_actor(). A non-admin who bypasses the client-side guard still gets a 403 from every request.
What the server actually checks. Your browser session never talks to the API directly: the console's server side mints a short-lived, HMAC-signed actor assertion carrying your email, organization and role, and the API resolves your role from your own user record through it. The role it finds must carry the admin scope, and the result is additionally capped by the scopes of the credential the console itself presents - so a browser session can never grant itself more than the console has. AUTH_ADMIN_EMAILS is what assigns the admin role to your session when you sign in; it is not what the API checks. See Actor assertions for the mechanics.
If a page shows an error instead of data:
- 403 means your account's role is not
adminon this deployment. If you should be an admin, ask whoever manages the deployment to add your email toAUTH_ADMIN_EMAILS(which sets the role at your next sign-in) or to change your role from Settings > Admin > Users. - 404 on a resource means that admin feature isn't enabled on this deployment.
Users¶
Settings > Admin > Users lists the users of your organization - avatar/name/email, sign-in provider, enabled status, last seen, and joined date.
- Invite User adds an email address to your organization with a role: Member, Admin, or Viewer. If the address is new to the deployment an account is pre-provisioned for it. No email is sent and no password is set by this dialog - tell the person to sign in to the console with that address through your organization's sign-in method; their profile fills in on first sign-in. To send them a link as well, use an invitation.
- Roles are per organization. The role you pick in the dialog is the person's role in your organization. Change it later under Admin > Organizations, on that organization's member list. The Users table has no role control.
- Disable / Enable immediately blocks or restores sign-in for that user. Disabling is confirmation-gated with a warning that it signs the user out immediately.
- Remove permanently deletes the user's console access. The server explicitly cascades the delete - the user's API keys and org memberships are removed in the same transaction, so nothing is left orphaned. This cannot be undone.
- Self-lockout protection: you cannot disable your own account or remove yourself. Those controls are disabled (with a tooltip) on your own row.
Organizations¶
Settings > Admin > Organizations lists the tenant's organizations (Org ID, Name, Member count, Created date). The page needs a tenant admin: listing the tenant's organizations, managing one other than your own and creating one are a tenant admin's. An organization admin who is not a tenant admin sees an error here instead of the list.
- New Organization creates an org from a Name (the Org ID auto-slugifies from the name - edit it manually if you need something specific; it cannot be changed after creation). Tenant admins only. The tenant admin who creates it becomes its first admin.
- Click a row (or Expand) to see its members inline: email, a role dropdown (
admin/member/viewer), and a Remove action. - Changing a member's role or removing a member is confirmation-gated.
- Delete asks you to type the organization's name. If the delete is refused, the confirmation lists what blocks it (unfinished runs, enabled schedules, scan agent credentials and more). See Deleting an organization.
- Last-admin protection: the server refuses to demote or remove the org's last remaining
adminmember - the request fails rather than leaving the org with zero admins. It also refuses a demotion or removal that would leave the tenant with no tenant admin.
Invitations¶
An invitation tells a person that an admin has given them access. It is a single-use link to the console that works for seven days and only for the address it was made for. It grants nothing itself: the person must already be a member of the organization, and opening the link while signed in as that address records that they accepted.
- Invite / Resend on a member's row (expand the organization under Settings > Admin > Organizations) creates the link. Organization admins only.
- The link is shown once, in the dialog. Copy it then; it cannot be retrieved again. Creating a new invitation for the same person ends the previous link.
- The dialog also emails the link when your administrator has configured an outgoing mail server for the deployment. The dialog says whether the mail was sent. Without that mail setting nothing is sent, and the link shown in the dialog is the only copy: give it to the person yourself.
- Pending invitations lists the links that can still be used, and Revoke ends one. Revoking does not remove the member.
- At most five invitations an hour for the same person and fifty an hour for the organization.
- A member who is disabled, or whose address cannot receive an invitation, is refused with
409invitation_not_deliverable, and the member stays. That covers the built-inadminaccount, which has no mail address, and an address that is not in Unicode normalization form NFKC once lower-cased (for example a capital letter followed by a separate combining accent): its link could never be accepted. The same address with a precomposed letter, where the script has one, works.
Older records that belong to no organization¶
Runs, schedules, remediations and API keys created before organizations existed may have no organization. They belong to no tenant, on every deployment: they do not appear in any organization's lists and opening one by its id answers "not found". Nothing assigns them an organization automatically. Your platform operator can list them and, for a schedule, give it an organization.
An API key with no organization no longer works. A request that presents one is answered with 401 "API key belongs to no organization; ask the platform operator to issue a new key". Create a replacement under Settings > Admin > API Keys (or ask the owner of the key to create one under their own settings) and update the caller. The old key is not listed there and cannot be deleted; it stays unusable.
See Teams for dividing an organization into scoped sub-groups once it has more than one member.
Switching organizations¶
This isn't admin-only: any user who belongs to more than one organization gets an organization picker in the top bar. Switching organizations clears all cached data in the console and re-fetches everything scoped to the newly selected org: findings, runs, connections, and settings for one organization are never visible from another.
Note: On multi-tenant (SaaS) deployments, this isolation is additionally enforced at the database level via row-level security, independent of anything the UI does, so a client-side bug can't leak another organization's data even in principle. Single-tenant, self-hosted deployments generally run with a single default organization and this doesn't come into play.
API Keys¶
Settings > Admin > API Keys lists every long-lived API key in the org - Name, Owner, Scopes, Created date.
- Create API Key now requires selecting a user from a dropdown (the key is issued as that user), a key name, and a scope selection. The raw key is shown once, immediately after creation, with an explicit "copy it now" warning - it is never retrievable again afterward.
- Edit Scopes opens the scope checklist for an existing key. Assignable scopes:
read,write,schedule:write,remediation:approve,findings:write,admin,debug. - Both dialogs ask for an extra confirmation before they grant an escalating scope:
- The escalating scopes are
admin,debugandremediation:approve- and any scope the console does not recognise, which it treats as privileged rather than harmless.read,write,schedule:writeandfindings:writeare the only scopes that never confirm. - Only a newly granted scope confirms: one selected on a new key, or one added to a key that did not have it before. Re-saving a key that already holds the scope does not.
- The confirmation names every scope it is about to grant and says what each allows -
admin: manage every user, API key, service account, and organization in the console;debug: dispatch agents directly and read task state and Redis keys through the debug API;remediation:approve: approve automated remediation. A scope the console does not recognise is called out as unrecognised. - Removing a scope, or an edit that adds only
read,write,schedule:writeorfindings:write, saves immediately with no extra confirmation. - Revoke permanently deletes the key; confirmation-gated, cannot be undone.
- Keys are listed and revoked in the organization your request acts in. A key created before organizations existed, with no organization, is not listed and no longer authenticates; see Older records that belong to no organization.
Service Accounts¶
Settings > Admin > Service Accounts lists OAuth2 client-credential accounts used for machine-to-machine API access (CI pipelines, external integrations) - Name, Client ID, Credentials type (always shown as OAUTH2), Status (ENABLED/DISABLED), Created date.
- Create Service Account takes a name and scopes. On success, the Client ID and Client Secret are both shown once - copy them immediately; the secret cannot be retrieved again.
- Rotate issues a new client secret. The previous secret keeps working for a 1-hour grace period so in-flight integrations don't break mid-rotation; the new secret is shown once, with the grace period noted in the dialog. Rotation also revokes outstanding refresh tokens for the account and writes an audit entry.
- Disable / Enable immediately blocks (or restores) every API call authenticated with that account's credentials. Disabling is confirmation-gated.
- Create Service Account and Edit Scopes confirm the same way as API Keys - newly granting
admin,debug,remediation:approveor a scope the console does not recognise requires the extra confirmation step, which names each scope being granted (a service account credential bypasses the console session check entirely, so a privileged one is especially sensitive). - Revoke permanently deletes the service account; confirmation-gated, cannot be undone.
This page's route (/admin/service-accounts) is registered unconditionally as a general admin-console feature - it does not require EXTERNAL_API_ENABLED to be set. (EXTERNAL_API_ENABLED separately gates the public Swagger UI, ReDoc, OpenAPI JSON, and the POST /auth/token OAuth2 token-issuance endpoint that service accounts use to actually authenticate against the external API - the admin CRUD you use to manage accounts here works regardless of that flag.)
Destructive actions
Every destructive or consequential action in the admin console (disable, remove, revoke, role change, scope escalation) uses the same inline confirm-row or confirm-block pattern - never a browser-native confirm dialog - with explicit copy describing the blast radius before you commit.