Roles & permissions
WatchTower15 uses role-based access control (RBAC) scoped to each organisation. Permissions are granular and per-module; roles bundle permissions; users hold one or more roles. Everything is enforced server-side on every request, so the UI and the API share exactly one source of truth.
The model
Section titled “The model”- Organisation-scoped. Roles and assignments belong to an organisation. A role in one organisation has no effect in another — a user can be an admin in one org and read-only in another with no bleed-through.
- Permissions are the atoms. Each permission is a
module.tierstring. Tiers areview,edit(create + update), anddelete. - Roles bundle permissions. A role is a named set of permissions.
- Users can hold multiple roles. Effective access is the union of their roles’ permissions.
Built-in roles
Section titled “Built-in roles”Every organisation is seeded with two system roles that cannot be renamed or deleted:
| Role | Access |
|---|---|
| Admin | Full access. Enforcement grants admins everything via a fast super-user bypass, so admins are never blocked by a missing individual permission. |
| Regular Users | Read-only starter role: servers.view, server_profiles.view, domains.view, database.view, logs.view, services.view. Log sources, custom metrics, alarms and incidents are not included; an Admin can add their view permissions to this role, or grant them through a custom role. Without custom_metrics.view, a server’s page does not show the custom metrics linked to it. A server’s and a database’s own alarms and incidents are still readable on its page (see below). New members default to this role on invitation. |
The person who registers an organisation holds its Admin role, so the first member can invite everyone else and assign their roles. Registering another organisation with an existing account makes you its Admin too, and leaves your roles in your other organisations as they are.
You can create any number of custom roles — e.g. a “Domain manager” with the domain and DNS permissions, or an “On-call” role with incident and policy access — and assign several to a user as needed.
The permission catalogue
Section titled “The permission catalogue”WatchTower15 ships a catalogue of granular permissions across every functional module:
| Module | Permissions |
|---|---|
| Servers | servers.view · servers.edit · servers.delete |
| Server profiles | server_profiles.view · server_profiles.edit · server_profiles.delete |
| Domains | domains.view · domains.edit · domains.delete |
| Services | services.view · services.edit · services.delete · services.parameters |
| Logs | logs.view |
| Log sources | log_sources.view · log_sources.edit · log_sources.delete |
| Custom metrics & metric sources | custom_metrics.view · custom_metrics.edit · custom_metrics.delete |
| Databases | database.view · database.edit · database.delete |
| Incidents | incidents.view · incidents.edit · incidents.delete |
| Alarms | alarms.view · alarms.edit · alarms.delete |
| Alarm policies | policies.view · policies.edit · policies.delete |
| Notification channels | channels.view · channels.edit · channels.delete |
| Contact groups | contact_groups.view · contact_groups.edit · contact_groups.delete |
| Tags | tags.view · tags.edit · tags.delete |
| Users | users.view · users.edit · users.delete |
| Settings | settings.view · settings.edit |
| Billing | billing.view · billing.edit |
| Audit | audit.view |
edit covers both creating and updating; delete is separated so you can grant
change-without-destroy access — a common requirement for junior operators.
services.parameters separates changing a service’s check from deciding what the check may
read on your servers. It is needed, beside services.edit, to add a service with
parameters or with authentication other than None, and to change a
service’s parameters or authentication. A save that leaves them as they were (a kept password
or token included) needs only services.edit, so a member without services.parameters can
still create and edit services that read nothing. Admins have it; grant it to other roles
yourself.
Some screens follow a permission too. A menu item shows only to members who can open its page, and a pasted link to a page you can’t open takes you to the Dashboard:
| Menu item | Needs |
|---|---|
| Servers | servers.view |
| Domains | domains.view |
| Databases | database.view |
| Logs | logs.view |
| Metrics | custom_metrics.view |
| Services | services.view |
| Incidents | incidents.view |
| Policies | policies.view |
| Channels | channels.view |
| Contact Groups | contact_groups.view |
| Settings | settings.view |
| Users and Roles | users.view |
| Audit | audit.view |
| Tags | tags.view |
| Billing | billing.view |
Dashboard needs none, so the default Regular Users role sees Dashboard, Servers, Domains, Databases, Logs and
Services. A link or button to a page you can’t open is hidden too: Manage policies → in a resource’s alarms needs
policies.view, the Servers page’s Profiles button server_profiles.view, and Add and Edit on Domains,
Services and Server Profiles their .edit permission. On a server’s page, the ⋯ menu’s actions and the edits
made in place — its name, its notes, and (change) on its Profile and Tags cards — need servers.edit, except
Delete, which needs servers.delete; changing its profile also needs server_profiles.view, and the ⋯ menu is
hidden when it has nothing you can use (Changing a server). The Tags filter on list pages needs tags.view, and is
hidden without it. On the Logs page, the Databases group of Sources needs database.view beside logs.view
(below). A dashboard widget’s
By tag picker needs only the view permission for its resource type, such as
domains.view for a domain widget.
Databases: database.view reads them, their tabs included (the
Logs tab and the Info tab’s Logs card among them),
database.edit adds them and changes their setup (including Test again, on the Collectors
tab, the Cloud metrics card and the Logs card, Reassign now, Pause, Resume and Accept new
key), and database.delete deletes them. A server’s Databases card, listing the databases it
collects or runs, needs both servers.view and database.view.
A database’s slow and error logs on the Logs page, chosen
as a source beside the log lines, need both logs.view and database.view. Without
database.view, Sources has no Databases group, a saved view or a shared link opens
without its databases, and the API answers 403 to a request naming one. database.view alone
does not open the Logs page: it reads each database’s own Logs tab.
Custom metrics shown on a server need both
servers.view and custom_metrics.view: the server’s Custom metrics tab and its Overview
card appear only to someone holding the two. On a metric’s or source’s page, the servers it is
shown on are named only with servers.view; without it, the page gives their number.
The Audit tab and the audit trail
Section titled “The Audit tab and the audit trail”The Audit page and a resource’s Audit tab are one list: the tab is the page locked to that resource (Audit trail).
audit.viewopens the organisation’s Audit page — every event, with each request’s IP address, user agent and URL. On a resource’s Audit tab it adds the same: the IP Address column and, in an opened event, the user agent and URL, with the User filter and Open in Audit Trail →. Without it, the IP address, user agent and URL never reach your browser.- A resource’s Audit tab has no permission of its own. It follows the resource’s
viewpermission —servers.view,database.view,domains.view,services.view,log_sources.view,server_profiles.view, orcustom_metrics.viewfor custom metrics and metric sources. Whoever can view the resource sees its whole Audit tab, including its incident and alarm events. Changes to the servers a custom metric or metric source is shown on are on that metric’s or source’s Audit tab. users.viewlinks each person’s name to their page, on the Audit page and on every Audit tab, and gives the tab its User filter.- An incident event links to the incident’s page on the Audit page, for holders of
incidents.view. On an Audit tab it links the resource’s own incidents instead: a server’s or a database’s with its view permission alone, a domain’s, service’s, log source’s or custom metric’s withincidents.viewtoo. - Acting on those events needs its own permission. Acknowledging or resolving an
incident needs
incidents.edit, and changing an alarm needsalarms.edit.
A server’s alarms and incidents
Section titled “A server’s alarms and incidents”Anyone who can view a server — servers.view — reads its alarms and incidents on the
server’s page, without alarms.view or incidents.view: the alarm counts in the
header, and the Alarms and Incidents tabs, covering the server and its volumes,
listening ports and system services. They can evaluate the server as a whole, but not
act on it:
- Acknowledging or resolving an incident needs
incidents.edit. Adding or changing an alarm needsalarms.edit, and deleting onealarms.delete. - The Incidents menu item, its list of every incident in the organisation, and an
incident’s own page need
incidents.view, and so does seeing who was notified, and on which channel. Withoutincidents.view, an incident on the server’s page reads as text, with who acknowledged or resolved it.
A database’s alarms and incidents are readable the same way: anyone with database.view reads
the alarm counts in its header, its Alarms tab and its Incidents tab, without
alarms.view or incidents.view (see A database’s alarms and
A database’s incidents). Acting on them needs the same
permissions as on a server’s page. The alarms and incidents of a domain, service, log source or
custom metric need alarms.view and incidents.view. And the custom metrics
linked to a server are not the server’s own: its Custom metrics tab and card also need
custom_metrics.view.
Alarm policies need policies.view, everywhere. On every resource — a server, domain,
service, log source or custom metric, and a server profile’s alarm templates — someone without
policies.view sees how many policies notify an alarm, not which ones, and not who or what they
notify. On a resource’s Audit tab, a change to an alarm’s policies reads as a count, such as
+2 policies, unless you also hold audit.view. The Admin role sees everything.
How enforcement works
Section titled “How enforcement works”On every authenticated request, WatchTower15:
- Resolves the caller’s organisation context (the team the request targets).
- Grants immediately if the caller holds the Admin role.
- Otherwise checks whether any of the caller’s roles include the specific
permission the endpoint requires (e.g.
domains.editto update a domain). - Returns 403 Forbidden if not.
Because the check is per-endpoint and server-side, there’s no way to reach a protected action by calling the API directly — the browser and the API are held to the same permission.
Working with roles
Section titled “Working with roles”- Invite a member — they start as Regular Users (read-only).
- Assign roles — give them one or more roles; access is the union.
- Create custom roles — scope a role to exactly the modules a job needs.
- Delete guardrails — system roles can’t be removed, and a role still assigned to members can’t be deleted until it’s unassigned.
Related
Section titled “Related”- Audit trail — who did what, gated by
audit.view; each resource’s Audit tab follows itsviewpermission. - Single sign-on — how members authenticate.