Skip to content

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.

  • 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.tier string. Tiers are view, edit (create + update), and delete.
  • 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.

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.

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 page and a resource’s Audit tab are one list: the tab is the page locked to that resource (Audit trail).

  • audit.view opens 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 view permission — servers.view, database.view, domains.view, services.view, log_sources.view, server_profiles.view, or custom_metrics.view for 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.view links 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 with incidents.view too.
  • Acting on those events needs its own permission. Acknowledging or resolving an incident needs incidents.edit, and changing an alarm needs alarms.edit.

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 needs alarms.edit, and deleting one alarms.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. Without incidents.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.

On every authenticated request, WatchTower15:

  1. Resolves the caller’s organisation context (the team the request targets).
  2. Grants immediately if the caller holds the Admin role.
  3. Otherwise checks whether any of the caller’s roles include the specific permission the endpoint requires (e.g. domains.edit to update a domain).
  4. 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.

  • 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.
  • Audit trail — who did what, gated by audit.view; each resource’s Audit tab follows its view permission.
  • Single sign-on — how members authenticate.