Users, roles & SSO
Yagra ships with role-based access control from the first release.
Every account — local, SSO-provisioned, or an API token — carries one of three roles. Every state-changing action is permission-checked, and everything that changes state lands in an audit log.
This page covers the whole access-control surface: roles and permissions, local accounts and their session lifecycle, single sign-on, API tokens, and how the public-dashboard mode interacts with all of it.
Three roles, in strictly increasing order of privilege — each role holds everything the one below it holds:
- Viewer — read-only: inventory, metrics, dashboards, topology, events, and alerts. A Viewer can look at everything the WebUI shows and change nothing.
- Operator — Viewer plus running the monitoring: acknowledge or mute alerts, run Troubleshoot analyses and AI root-cause explanations, open or close maintenance windows, and decide what is monitored — nodes, groups, profiles, thresholds, collection, discovery, event rules and reports — including the credentials Yagra uses to reach devices. This is the on-call role.
- Admin — Operator plus the deployment itself: where data is sent, what the process runs as, and who may sign in. Notification delivery and routing, forwarding, the TLS certificate, upgrades, data retention, the AI provider, the configuration and support bundles, the poller inventory, user management, and the audit log.
The WebUI shows the full role-versus-privilege matrix under Settings ▸ Roles & privileges.
One recent change is worth knowing if you assigned roles under an earlier release: an Operator can now run the monitoring (v0.2.10).
Adding a node, editing a threshold, running a discovery sweep and changing a device profile were all Admin-only, because one permission — Manage configuration — covered both “decide what is monitored” and “change the deployment”. An Operator could respond to an incident but could not configure the monitoring that raised it.
That permission has been split. Manage monitoring and Manage credentials are now held by Operator and up; a new Manage the deployment stays Admin-only and covers everything that changes the deployment or sends its data somewhere else.
Review your Operator accounts before upgrading — this widens what they can do. Viewer and Admin are unchanged.
Permissions & group scoping
Section titled “Permissions & group scoping”Roles are bundles of permissions. The full set:
| Permission | What it allows | Viewer | Operator | Admin |
|---|---|---|---|---|
| View | View inventory, metrics, and alerts within scope | ✔ | ✔ | ✔ |
| Respond to incidents | Acknowledge or mute alerts, close event alerts, run Troubleshoot analyses, request AI root-cause explanations | ✔ | ✔ | |
| Manage maintenance | Open or close maintenance windows | ✔ | ✔ | |
| Manage monitoring | Decide what is monitored and when to alert: nodes, groups, profiles, thresholds, collection, discovery, event rules, and reports | ✔ | ✔ | |
| Manage credentials | Create, edit, and rotate monitoring credentials | ✔ | ✔ | |
| Manage the deployment | Change the deployment and anything that leaves it: notification delivery, forwarding, the TLS certificate, upgrades, data retention, the AI provider, the configuration bundle, the support bundle, and the poller inventory | ✔ | ||
| Manage users | Manage user accounts and role assignments | ✔ | ||
| View audit log | Read the audit log (who changed what) | ✔ |
Permissions attach to roles, not to individual accounts. You choose one of the three roles per account, and the matrix above decides the rest.
Group scoping. Alongside its role, an account carries a visibility scope: all groups, or a named set of node groups.
The scope declares which slice of the inventory the account is about. A node that belongs to no group at all falls only under the all-groups scope.
The API’s read endpoints do filter by the caller’s group scope. Node lists narrow to the allowed groups and everything beneath them, and aggregates and rankings are filtered to the same set.
A node outside the scope answers 404 — the same answer an unknown id gets, so the id space cannot
be probed.
Writes that name one node follow the same rule. Deleting, renaming, moving or re-pooling a node
outside the scope answers 404, and so does adding a check or a collection item to one. A move into
a folder outside the scope is refused as well, so a scoped account cannot put a node where it can no
longer see it.
A few endpoints have no per-node attribution left to filter: a rendered report, a pre-summed fleet timeline. Those refuse a group-scoped caller, rather than quietly serving fleet-wide numbers.
Two families of write refuse one outright, for the same reason. Credentials are filed in no
group — one community string, or one API key, is what every site is polled with — so creating,
changing or deleting one answers 403 to a scoped account. Reading the list still answers, which is
what the credential pickers on the node dialogs and on Discovery need, so a scoped operator can
still bind an existing credential to their own nodes. Every Cisco Meraki write refuses a scoped
account as well: an organization is monitored as a whole, its key sees every device in it, and a
device nobody has imported yet belongs to no group.
Assigning a scope. Under Settings ▸ Users, the row action Change scope limits an account to the groups you tick. Leaving all of them unticked restores fleet-wide visibility.
Accounts start unrestricted, including SSO accounts, which are provisioned on first sign-in and narrowed here afterwards. The assignment survives every subsequent login: it is a Yagra-side decision, not something re-derived from your directory.
Two rules are worth knowing before you use it:
- An Admin cannot be scoped. Administration is fleet-wide — an admin can reconfigure any node — so a narrowed admin would read an inventory that hides nodes it can still edit. Promoting an account to Admin clears whatever scope it held.
- A scope naming no groups is refused, rather than stored. It would otherwise be an account that signs in successfully to an empty inventory, with nothing on screen to explain why.
Saving a scope signs the account out of its current sessions, the way a role change does. The scope is captured in the session token, so a live one would keep the old, wider view.
The account menu says so when the account you are signed in as is limited. A scoped operator’s lists are simply shorter, with nothing else to distinguish “you can see three sites” from “there are three sites”.
Local accounts
Section titled “Local accounts”A fresh install bootstraps one local account, admin, and prints its initial password
once in the core logs on first start:
docker compose logs core # look for the one-time admin passwordSign in with it, then change it.
Local accounts are managed under Settings ▸ Users: create accounts with a role, change or reset passwords (“Change password”), disable an account, or delete it.
Changing your own password. You do not need an administrator for this. The account badge in the top right offers Change my password, between Preferences and Log out. It asks for your current password as well as the new one, so a stolen session cannot take the account with it.
The item is not shown for an account that signs in through LDAP or an identity provider — those have no password Yagra holds — and the badge says where the password lives instead.
Session lifecycle. Logging in issues a bearer session token, which the WebUI holds for you. Sessions are hardened in the ways you would expect of a system that pages people:
- Sessions expire, after an idle period and after an absolute lifetime, whichever comes first. Logging out revokes the token server-side immediately.
- Admin actions cut sessions off at once. Disabling an account, demoting its role, resetting its password, or deleting it invalidates that account’s active sessions on the spot. An already-issued token does not linger.
- The login endpoint applies brute-force protection: a per-account exponential lockout after repeated failures, plus a global attempt-rate cap. Password-guessing runs are throttled rather than serviced.
In a high-availability deployment with a shared session signing key, a login is accepted by every core and survives a core restart or failover. A logout, disable, role change, or password reset takes effect across all cores right away.
Single sign-on
Section titled “Single sign-on”Yagra signs users in against an external identity provider over OpenID Connect: Google Workspace, Microsoft Entra, Okta, Keycloak, or any other OIDC-capable IdP. This runs alongside local accounts, not instead of them.
Configure a provider under Settings ▸ Authentication. Start by picking which identity provider it is — Microsoft Entra ID, Okta, Google Workspace, or “Other” for any other OIDC provider.
The form then asks only for what that product actually needs, rather than presenting eight free-text fields and assuming you already know what your IdP wants:
-
Entra ID asks for the directory (tenant) ID and builds the issuer URL from it. It requests only the standard OIDC scopes, because Entra rejects any non-standard scope outright — a generic form that pre-filled
groupsnever reached a sign-in page at all.Enable the groups claim in the app registration’s token configuration. It arrives as group object IDs.
-
Okta asks for the org domain and builds the issuer URL from it, requesting the
groupsscope its org authorization server serves. A custom authorization server has a different issuer and belongs under “Other”. -
Google Workspace has one issuer, so there is no URL to enter. It also has no group→role mapping, deliberately: Google does not put group membership in the ID token, so a mapping configured against it could never match.
A default role is required instead, since without one every sign-in would be denied.
-
Other asks for the full set directly: issuer URL, client ID and client secret from your IdP’s app registration, plus the redirect URL Yagra should be called back on and the scopes to request.
Except on Google Workspace, a provider carries a mapping from IdP groups to Yagra roles, so membership in your directory decides whether someone lands as Viewer, Operator, or Admin.
Accounts are provisioned just in time. The first successful SSO sign-in creates the Yagra account.
The account’s role then follows the IdP’s group mapping on every login, so moving someone between directory groups updates their Yagra role the next time they sign in. Once a provider is enabled, a “Continue with SSO” button appears on the login screen.
The client secret is envelope-encrypted at rest and never shown again after it is saved — the same treatment Yagra gives monitoring credentials.
LDAP / Active Directory
Section titled “LDAP / Active Directory”For directories that are not OIDC providers, Yagra signs people in against LDAP or Active Directory directly. Configure it at Settings ▸ Auth ▸ Directory (LDAP/AD).
There is no second button and no separate URL. People use the ordinary login form with their corporate credentials.
Yagra searches for the person with a service account and then re-binds as the entry it found, so no DN pattern has to be guessed for your directory’s layout.
Group membership maps to a Yagra role through the same mapping the SSO provider uses, matching a group by its full DN or just its name. An account is created on first successful sign-in.
- Local accounts are always tried first. A directory that is unreachable can never lock an administrator out. Keep one local admin and a rollback stays survivable.
- LDAPS and StartTLS only, with a field for your private CA. There is deliberately no way to skip certificate verification.
- The bind password is envelope-encrypted at rest, like every other secret.
- A Test button reports each stage separately. Given a username, it shows the DN, the groups, and the role that person would receive — including when the answer is “denied”, which the login form otherwise reports as an ordinary wrong password.
An API token owned by a directory account expires with its owner’s silence, the same way an SSO-owned
one does (YAGRA_PAT_OIDC_IDLE_DAYS). A directory disabling somebody is not something Yagra is told
about, so the owner going quiet is the only signal there is.
SAML is answered with a bridge rather than an implementation. Put Keycloak or Dex in front as a SAML→OIDC bridge. Yagra does not verify XML signatures itself.
API tokens
Section titled “API tokens”For unattended clients — an MCP-connected AI assistant, a script, a CI job — Yagra issues long-lived
personal access tokens with a yat_ prefix.
An admin mints them under Settings ▸ API tokens. The raw token value is shown exactly once at creation, and only its hash is stored.
A token is defined by five things:
-
Surfaces. Which of the MCP endpoint and the REST API it may authenticate.
A token created before this field existed carries
mcpalone, so upgrading never widens an existing credential. Granting REST is an explicit choice at creation. -
An owner. The account the token acts as. A token can never exceed its owner in either dimension.
Its effective role is the lower of the token’s role and the owner’s current role, and its scope is capped at the owner’s the same way. So demoting or narrowing the account narrows the token at once, and disabling or deleting the account revokes it.
-
A role. Pick the least-privileged one. A Viewer token covers every read; Operator adds acknowledging alerts and running analyses.
Two things no token can do regardless of role: administer users, and use endpoints that mean “the signed-in account”. The first matters most — a credential that could mint its own successor would outlive revoking the original.
-
A scope. Which node groups the token may see. It is the same group scoping an account carries, and is honoured on both surfaces. Leave it unset for the whole fleet.
-
An optional expiry. Omitting it means no expiry, which is the right answer for a service-account credential driving an integration.
A token cannot exceed its owner, so a token owned by an already-scoped account inherits that
account’s scope and cannot be given a different one (400 owner_is_scoped).
That is the containment you usually want. Narrowing the account narrows every credential it holds, immediately and with nothing to re-issue. To give one token a narrower view than another, own each with its own service account, scoped to what that token should see.
Service accounts
Section titled “Service accounts”An unattended credential should not belong to a person. When they change teams, the integration goes with them.
A service account (Settings ▸ Users & roles ▸ Add user, account type Service account) is a machine identity with no password, which cannot sign in through either the local form or SSO.
It exists to own API tokens, so that a token survives staff changes and so that disabling one account stops every credential it owns at once.
The guard that prevents removing the last admin counts only accounts a person can sign in with, so a service account can hold the Admin role without becoming the only administrator.
Issuing and revoking tokens are admin actions, and both are audited. Every MCP write made with a token records an audit entry naming it, and a token can be revoked at any time from the same page. See Connecting an AI client for putting a token to use.
The audit log
Section titled “The audit log”Every state-changing action is recorded in an append-only audit log — who did what, when. That covers creating or editing configuration, acknowledging an alert, opening a maintenance window, managing users and tokens, and every login. Saving your own WebUI preferences — closed folders, column widths — is not recorded, because it changes nothing but your own screen.
Writes made through MCP tools are audited the same as writes made through the WebUI, so an AI assistant acting on an Operator token leaves the same trail an operator does.
Admins read it under Settings ▸ Audit log.
Public dashboard mode
Section titled “Public dashboard mode”Yagra can serve one board to visitors who have no account. Anonymous viewing is a setting an admin turns on at Settings ▸ Sign-in methods ▸ Public dashboard, and it is off by default. What goes out is the board composed at Dashboard ▸ Public dashboard — one board, not the read API.
Composing that board takes Admin (manage_system), a step above the shared board’s
manage_config, because what goes on it decides what strangers can read.
The mode opens reads only, and only the ones that board needs:
- The board defines the surface. The routes open to an anonymous caller are derived from the widgets placed on the public board, so removing a widget closes the routes it read. Nothing that no widget asks for is reachable.
- Every write still requires an authenticated session and the matching permission. Anonymous viewers can look, not touch.
- MCP always requires a token, even with the public dashboard on. The
/mcpsurface returns unfiltered monitoring data, so it stays authenticated unconditionally. - A few reads stay closed because of what they reveal. The threshold rule set, for example, describes when and whom Yagra will page, so reading it requires the manage-monitoring permission and is not exposed to anonymous viewers.
Anonymous visitors land on this sign-in screen, not on the board. When a board is published, the sign-in screen carries a Show public dashboard button through to it — so a public deployment still offers an operator an obvious way in.
See also
Section titled “See also”- REST API — authentication, the error contract, and the published OpenAPI document.
- Connecting an AI client (MCP) — enabling
/mcpand registering ayat_token with Claude Code, Claude Desktop, or another MCP client. - Security — credential encryption at rest, TLS guidance for the WebUI and API, and what leaves the box.