Roles & Permissions
EnterpriseDefine roles from the 14 NetStacks permission keys, build custom roles, and understand how the users.manage-gated RBAC controls access across the Controller.
Overview
NetStacks Controller uses role-based access control (RBAC) to govern what each user can do. A role is a named list of permission keys stored as a JSON array. When a user attempts an action — through the Admin UI, the Terminal, or the API — the Controller checks whether the user holds the permission key required by that endpoint.
The system seeds three roles in the default organization — admin,operator, and viewer. The admin role is protected (it cannot be deleted) and holds the single wildcard permission *, which grants everything. You can edit any role's permissions, and you can create additional custom roles from any combination of the 14 granular permission keys.
A user may hold any number of roles. Their effective permission set is the union of all permissions from all assigned roles. Permissions are never subtracted — if any role grants a key, the user has it. Holding the* wildcard in any role grants every permission.
Roles and RBAC apply to the NetStacks Controller (Enterprise). Standalone Terminals run with local capabilities and do not call the Controller's permission system.
Permission Catalog
There are 14 granular permission keys plus the * wildcard. This is the complete, authoritative catalog — the same list returned by the GET /api/admin/roles/permissions endpoint and rendered in the role editor. No other keys exist; any key outside this list is invalid.
| Key | Label | Description | Category |
|---|---|---|---|
devices.access | Device Inventory | View device inventory and details | Devices & Network |
devices.manage | Device Management | Create, edit, delete devices and NetBox sync | Devices & Network |
snapshots.manage | Config Snapshots | Run and manage config snapshots and backups | Devices & Network |
config.deploy | Config Deployment | Deploy stacks, templates, and config changes to devices | Devices & Network |
topologies.access | Topologies | View and edit network topologies | Devices & Network |
sessions.connect | SSH/Telnet Sessions | Open SSH and Telnet sessions to network devices | Sessions & Connectivity |
agents.manage | AI Agents | Create, edit, and run AI agent tasks | AI & Automation |
scripts.run | Scripts | Execute user scripts on devices | AI & Automation |
mops.manage | MOPs | Create, edit, and execute Methods of Procedure | AI & Automation |
knowledge.access | Knowledge Search | Search the knowledge base | AI & Automation |
knowledge.manage | Knowledge Management | Upload, edit, and delete knowledge base documents | AI & Automation |
users.manage | Users & Roles | Manage user accounts, roles, and role assignments | Administration |
credentials.manage | Credentials Vault | Manage the credential vault — create, edit, delete credentials | Administration |
system.manage | System Settings | System settings, license, backup/restore, plugins | Administration |
* | All Permissions | Full unrestricted access (wildcard) | — |
The permission that lets a user create, edit, delete, or assign roles is users.manage. There is no separate roles.* permission. Any role with users.manage (or *) can administer all roles.
Built-in Roles
The three seeded roles and their exact permission sets are listed below. Only admin is protected from deletion; operator and viewer can be edited or deleted like any custom role.
- admin (protected) —
["*"]. Full unrestricted access to every feature, including user/role management, settings, and audit logs. - operator —
devices.access,devices.manage,snapshots.manage,config.deploy,topologies.access,sessions.connect,agents.manage,scripts.run,mops.manage,knowledge.manage,knowledge.access. Operational access to devices, sessions, automation, and knowledge. Nousers.manage,credentials.manage, orsystem.manage. - viewer —
devices.access,topologies.access,knowledge.access. Authenticated read-level access to the device inventory, topologies, and knowledge search only.
Permission matrix
| Permission | admin | operator | viewer |
|---|---|---|---|
devices.access | Yes | Yes | Yes |
devices.manage | Yes | Yes | — |
snapshots.manage | Yes | Yes | — |
config.deploy | Yes | Yes | — |
topologies.access | Yes | Yes | Yes |
sessions.connect | Yes | Yes | — |
agents.manage | Yes | Yes | — |
scripts.run | Yes | Yes | — |
mops.manage | Yes | Yes | — |
knowledge.access | Yes | Yes | Yes |
knowledge.manage | Yes | Yes | — |
users.manage | Yes | — | — |
credentials.manage | Yes | — | — |
system.manage | Yes | — | — |
In the matrix above, admin shows “Yes” for every row because its stored permission list is the single * wildcard, which the permission check treats as granting all keys.
How It Works
Permission model
Permission keys follow a resource.action naming convention (for example devices.manage). The check is performed by the RequirePermission<P> request extractor in the API: it loads the user's permissions from PostgreSQL on every protected request and compares them to the key the endpoint requires.
Matching is exact, with one special case: the * wildcard grants everything. There is no category-level expansion — holding devices.manage does not imply devices.access, and there is no devices.* key. If an endpoint requires devices.access, the user's role list must contain either devices.access or *.
// Effective check, conceptually:
// 1. If the user has "*", allow.
// 2. Else if the user's permission list contains the exact required key, allow.
// 3. Else deny (403).
user.permissions = ["devices.access", "topologies.access"]
required = "devices.manage" // -> DENIED (no exact match, no "*")
required = "devices.access" // -> ALLOWED (exact match)Permission Grid UI
The role editor (Admin → Roles → a role) renders the catalog as a collapsible category grid populated from GET /api/admin/roles/permissions. The catalog has four categories:
- Devices & Network —
devices.access,devices.manage,snapshots.manage,config.deploy,topologies.access - Sessions & Connectivity —
sessions.connect - AI & Automation —
agents.manage,scripts.run,mops.manage,knowledge.access,knowledge.manage - Administration —
users.manage,credentials.manage,system.manage
An All Permissions master toggle at the top maps to the * wildcard and grants full unrestricted access. Each category header has a checkbox that toggles every permission in that group at once (it shows an indeterminate state when only some keys in the category are selected). While * is selected, all individual and category checkboxes are shown as checked and disabled, since the wildcard already covers them.
Enforcement
Permissions are enforced server-side on every API request, and the Terminal and Admin UI additionally use them for progressive enhancement. At login, clients read the user's permission list from the GET /api/capabilities endpoint, which returns a permissions array alongside licensed feature flags. The UI hides or disables controls the user cannot use — for example, a user lacking sessions.connect does not get session controls — but the API check is the authority. Because permissions are read from the database per request, role changes take effect immediately, without re-login.
Step-by-Step Guide
Viewing roles
- Go to Admin → Roles.
- The list shows each role with its name, description, and a protected badge on the
adminrole. The Delete button is disabled for protected roles. - Click a role to open the editor and view or change its permissions in the grid.
Creating a custom role
- Go to Admin → Roles and click Create Role.
- Enter a name (must be unique within the organization) and an optional description.
- Open the new role and select permissions in the grid. For a NOC operator who should connect to devices and run automation but not touch the vault or settings, select:
devices.access,devices.manage,config.deploy,sessions.connect,scripts.run,mops.manage,knowledge.access. - Click Save Changes. The role is immediately assignable.
Grant only the keys a team needs. sessions.connect lets a user open SSH sessions to devices without granting credentials.manage — they can connect using stored credentials without being able to view, edit, or export the vault.
Updating a role
- Open the role from Admin → Roles.
- Adjust the name, description, or permission checkboxes.
- Click Save Changes. The new permissions apply to every assigned user on their next API request (permissions are loaded per request, not cached in tokens).
Removing a key from a role immediately revokes that access for every user holding the role. Open the role detail first to review its user count before editing.
Assigning roles to users
- Go to Admin → Users and open a user.
- Add or remove roles in the user's roles section.
- Save. The user's effective permissions (the union of all assigned roles) update immediately.
Code Examples
Role endpoints live under /api/admin/roles. Every role operation — list, get, create, update, delete — and the permission-catalog endpoint require the users.manage permission (the admin role has it via *).
List the permission catalog
This is the authoritative source for the grid. Use it to discover valid keys programmatically.
curl https://netstacks.dc1.example.net/api/admin/roles/permissions \
-H "Authorization: Bearer $TOKEN"{
"categories": [
{
"name": "Devices & Network",
"permissions": [
{ "key": "devices.access", "label": "Device Inventory", "description": "View device inventory and details", "category": "Devices & Network" },
{ "key": "devices.manage", "label": "Device Management", "description": "Create, edit, delete devices and NetBox sync", "category": "Devices & Network" },
{ "key": "snapshots.manage", "label": "Config Snapshots", "description": "Run and manage config snapshots and backups", "category": "Devices & Network" },
{ "key": "config.deploy", "label": "Config Deployment", "description": "Deploy stacks, templates, and config changes to devices", "category": "Devices & Network" },
{ "key": "topologies.access", "label": "Topologies", "description": "View and edit network topologies", "category": "Devices & Network" }
]
},
{
"name": "Sessions & Connectivity",
"permissions": [
{ "key": "sessions.connect", "label": "SSH/Telnet Sessions", "description": "Open SSH and Telnet sessions to network devices", "category": "Sessions & Connectivity" }
]
},
{
"name": "AI & Automation",
"permissions": [
{ "key": "agents.manage", "label": "AI Agents", "description": "Create, edit, and run AI agent tasks", "category": "AI & Automation" },
{ "key": "scripts.run", "label": "Scripts", "description": "Execute user scripts on devices", "category": "AI & Automation" },
{ "key": "mops.manage", "label": "MOPs", "description": "Create, edit, and execute Methods of Procedure", "category": "AI & Automation" },
{ "key": "knowledge.access", "label": "Knowledge Search", "description": "Search the knowledge base", "category": "AI & Automation" },
{ "key": "knowledge.manage", "label": "Knowledge Management", "description": "Upload, edit, and delete knowledge base documents", "category": "AI & Automation" }
]
},
{
"name": "Administration",
"permissions": [
{ "key": "users.manage", "label": "Users & Roles", "description": "Manage user accounts, roles, and role assignments", "category": "Administration" },
{ "key": "credentials.manage", "label": "Credentials Vault", "description": "Manage the credential vault — create, edit, delete credentials", "category": "Administration" },
{ "key": "system.manage", "label": "System Settings", "description": "System settings, license, backup/restore, plugins", "category": "Administration" }
]
}
]
}List all roles
curl https://netstacks.dc1.example.net/api/admin/roles \
-H "Authorization: Bearer $TOKEN"{
"roles": [
{
"id": "00000000-0000-0000-0000-000000000001",
"name": "admin",
"description": "Full system access - all permissions",
"permissions": ["*"],
"protected": true,
"created_at": "2026-01-01T00:00:00Z"
},
{
"id": "00000000-0000-0000-0000-000000000003",
"name": "operator",
"description": "Operational access to all features: devices, sessions, credentials, AI, tunnels, agents, MOPs, scripts, knowledge",
"permissions": [
"devices.access", "devices.manage", "snapshots.manage", "config.deploy",
"topologies.access", "sessions.connect", "agents.manage", "scripts.run",
"mops.manage", "knowledge.manage", "knowledge.access"
],
"protected": false,
"created_at": "2026-01-01T00:00:00Z"
},
{
"id": "00000000-0000-0000-0000-000000000002",
"name": "viewer",
"description": "Authenticated access only - no admin or operator privileges",
"permissions": ["devices.access", "topologies.access", "knowledge.access"],
"protected": false,
"created_at": "2026-01-01T00:00:00Z"
}
]
}Get one role (with user count)
curl https://netstacks.dc1.example.net/api/admin/roles/00000000-0000-0000-0000-000000000003 \
-H "Authorization: Bearer $TOKEN"{
"id": "00000000-0000-0000-0000-000000000003",
"name": "operator",
"description": "Operational access to all features ...",
"permissions": ["devices.access", "devices.manage", "config.deploy", "sessions.connect"],
"protected": false,
"created_at": "2026-01-01T00:00:00Z",
"user_count": 7
}Create a custom role
Returns the new role's id. Body keys: name, description (optional), permissions (array of valid keys).
curl -X POST https://netstacks.dc1.example.net/api/admin/roles \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "NOC Operator",
"description": "Device and automation access for the NOC team",
"permissions": [
"devices.access",
"devices.manage",
"config.deploy",
"sessions.connect",
"scripts.run",
"mops.manage",
"knowledge.access"
]
}'{ "id": "d4e5f6a7-b8c9-0123-def0-456789012345" }Update a role
Send only the fields you want to change. Replacing permissions overwrites the whole list, so include every key the role should keep. Returns { "success": true }.
curl -X PUT https://netstacks.dc1.example.net/api/admin/roles/d4e5f6a7-b8c9-0123-def0-456789012345 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permissions": [
"devices.access",
"devices.manage",
"config.deploy",
"sessions.connect",
"scripts.run",
"mops.manage",
"knowledge.access",
"agents.manage"
]
}'Delete a custom role
Protected roles (the seeded admin role) return 400 Bad Request.
curl -X DELETE https://netstacks.dc1.example.net/api/admin/roles/d4e5f6a7-b8c9-0123-def0-456789012345 \
-H "Authorization: Bearer $TOKEN"Read a user's effective permissions
curl https://netstacks.dc1.example.net/api/capabilities \
-H "Authorization: Bearer $TOKEN"{
"permissions": ["devices.access", "devices.manage", "config.deploy", "sessions.connect"],
"features": [ /* licensed feature flags */ ]
}Questions & Answers
- How many permissions does NetStacks have?
- Fourteen granular permission keys plus the
*wildcard:devices.access,devices.manage,snapshots.manage,config.deploy,topologies.access,sessions.connect,agents.manage,scripts.run,mops.manage,knowledge.access,knowledge.manage,users.manage,credentials.manage, andsystem.manage. Fetch the live list fromGET /api/admin/roles/permissions. - What permissions does the default admin role have?
- The
adminrole's stored permission list is the single wildcard["*"], which grants every key in the catalog. It is the only seeded role withusers.manage,credentials.manage, andsystem.manage, and it is the only protected role. - Which permission lets someone manage roles?
users.manage. Every role endpoint — list, get, create, update, delete — and the permission-catalog endpoint are gated on it. There is no separateroles.vieworroles.*permission.- Do wildcard keys like devices.* exist?
- No. The only wildcard is
*, which grants everything. Permission matching is otherwise exact:devices.managedoes not implydevices.access, and there is nodevices.*key. Grant each specific key you need. - Can a user have multiple roles?
- Yes. Effective permissions are the union of all assigned roles — never subtracted. Assigning both
viewerand a custom role gives the user every key from both. - How do I see a user's effective permissions?
- Call
GET /api/capabilitiesas that user (it returns apermissionsarray), or review each assigned role in Admin → Users. The effective set is the union of all role permission lists. - What happens when I remove a permission from a role?
- Every user holding that role loses the affected access on their next API request. Permissions are read from the database per request rather than cached in the token, so there is no delay and no re-login needed.
- Can I edit or delete the built-in roles?
- You can edit the permissions of all three seeded roles. You can delete
operatorandviewer. Theadminrole is protected and cannot be deleted (the API returns400), guaranteeing at least one administrative role always exists.
Troubleshooting
A user cannot access a feature
- Check the user's roles in Admin → Users.
- Confirm at least one assigned role holds the exact key the feature requires. For example, opening device SSH/Telnet sessions requires
sessions.connect; deploying configuration requiresconfig.deploy. - Remember there is no category expansion —
devices.managealone does not grantdevices.access. Add each key explicitly, or grant*.
403 Forbidden on an API call
- A
403means the token is valid but the user lacks the required permission. Each endpoint is guarded by aRequirePermission<P>extractor — for example, role and user endpoints useAdminPermission, which maps tousers.manage. - Permissions are loaded from the database on every request, so a recent role change applies without re-login. If it still fails, re-check the role's permission list.
Cannot delete the admin role
- The seeded
adminrole is protected.DELETEreturns400 Bad Request, and the Admin UI disables its Delete button. - You can still edit the
adminrole's permissions — only deletion is blocked. Other roles can be edited and deleted.
Role name already exists
- Role names must be unique within the organization. Creating or renaming to an existing name returns
400 Bad Request. An empty name is also rejected with400.
Created a role with no effect
- Verify the keys you sent are valid — only the 14 catalog keys and
*are recognized. Misspelled keys are stored but never match a permission check. Cross-check againstGET /api/admin/roles/permissions.
Related Features
- User Management — Create users and assign roles to control their effective permissions.
- Authentication — Configure local, LDAP, and OIDC sign-in for the accounts that roles are assigned to.
- Audit Logs — Track role changes via the
role.created,role.updated, androle.deletedevents. - API Authentication — How tokens and the per-request permission check enforce RBAC on every endpoint.
- Credential Vault — The vault managed by
credentials.manageand used oversessions.connect. - Auth Troubleshooting — Resolve login and permission issues, including 401/403 responses.