Roles
A Role describes a job someone does, as a list of things they're allowed to do. "Staging operator" might mean reading the staging configs and running the monitoring playbooks on them. "Auditor" might mean reading Scopes, Roles, and RoleBindings.
Each item in that list is a rule. A rule pairs one action, such as read or playbook:run, with the Scope of resources it applies to.
A Role doesn't give anyone anything by itself. You write it once, then give it to people, teams or playbooks with a RoleBinding. The same Role can be bound as many times as you like.
A first Role
This Role has a single rule: read every config in the john-catalog-read Scope.
john-catalog-read.yamlapiVersion: mission-control.flanksource.com/v1
kind: Scope
metadata:
name: john-catalog-read
namespace: default
spec:
targets:
- config:
name: "*"
---
apiVersion: mission-control.flanksource.com/v1
kind: Role
metadata:
name: john-catalog-read
namespace: default
spec:
description: Grant John read permissions to catalogs
rules:
- name: read-configs
action: read
resource:
scopeRef: john-catalog-read
---
apiVersion: mission-control.flanksource.com/v1
kind: RoleBinding
metadata:
name: john-catalog-read
namespace: default
spec:
role: john-catalog-read
subjects:
people:
- john@doe.com
The manifest has three parts:
- A Scope that selects every config.
- A Role with one rule,
read, on that Scope. - A RoleBinding that gives the Role to
john@doe.com.
The three objects share a name only so the example reads clearly. Names are independent.
A rule has these fields:
| Field | Meaning |
|---|---|
name | A name for the rule, unique within the Role. It appears in status messages |
action | What the rule allows, such as read or playbook:run |
resource.scopeRef | The Scope of resources the action acts on |
target.scopeRef | For playbook actions, the Scope of configs, components or checks a playbook may run against |
A Role works only while every one of its rules is valid. If one rule breaks, for example because its Scope was deleted, the whole Role stops granting and its status shows Ready=False with the reason.
Running playbooks: resource and target
Some actions involve two resources. When you run a playbook on a config, the playbook is what you act on, and the config is what you act against. A rule names each of them:
resourceis the Scope of playbooks that may be run.targetis the Scope of configs, components or checks they may run on.
john-retry-echo-run.yamlapiVersion: mission-control.flanksource.com/v1
kind: Scope
metadata:
name: retry-echo-playbook
namespace: default
spec:
targets:
- playbook:
name: retry-echo
---
apiVersion: mission-control.flanksource.com/v1
kind: Scope
metadata:
name: all-configs
namespace: default
spec:
targets:
- config:
name: "*"
---
apiVersion: mission-control.flanksource.com/v1
kind: Role
metadata:
name: john-retry-echo-run
namespace: default
spec:
description: allow user john to run retry-echo playbook on any config
rules:
- name: run
action: playbook:run
resource:
scopeRef: retry-echo-playbook
target:
scopeRef: all-configs
- name: approve
action: playbook:approve
resource:
scopeRef: retry-echo-playbook
target:
scopeRef: all-configs
- name: cancel
action: playbook:cancel
resource:
scopeRef: retry-echo-playbook
target:
scopeRef: all-configs
---
apiVersion: mission-control.flanksource.com/v1
kind: RoleBinding
metadata:
name: john-retry-echo-run
namespace: default
spec:
role: john-retry-echo-run
subjects:
people:
- john@doe.com
This Role lets John run, approve and cancel the retry-echo playbook on any config. Each action needs its own rule, which is why there are three.
A few things to keep in mind when granting playbooks:
- Running a playbook on a config also needs
readon that config. A run works with the data of the config, so whoever starts it must be able to see the config. Give John a separatereadrule, or bind a Role such asviewer. - A rule without a
targetonly covers runs that don't start from a config, component or check, such as a playbook you run from the playbooks list. To let a playbook run on configs, the rule needs atarget. - To find a playbook in the UI, John needs
readon it. The shippedviewerRole reads every playbook. - Keep each pairing in its own rule. A rule pairs its playbooks only with its own targets. If
echomay run on staging andrestart-podonly on production, write two rules, so neither playbook can run on the other's configs.
Common actions
| Action | Lets someone |
|---|---|
read | See resources, in listings and when opening them |
create, update, delete | Create, change or delete resources of a type |
playbook:run, playbook:approve, playbook:cancel | Run playbooks, approve runs that need approval, and cancel runs |
mcp:run | Run playbooks from an AI agent through MCP |
connection:use | Act with a connection's credentials, for example to send a notification |
mcp:use | Connect to Mission Control's MCP server |
The Actions reference lists every action, including the plugin actions and the few that act on Mission Control itself, such as person:manage for managing users.
Seeing a connection versus using it
Being able to see a connection and being able to use its credentials are different permissions:
readlets someone see a connection's name, type and settings. Credentials are always hidden.connection:uselets someone, or something, act with those credentials.
This matters most for playbooks and notifications. A notification that sends to Slack needs connection:use on the Slack connection. A person who only needs to choose a connection in the UI needs read. Reading connections is granted for every connection at once, while connection:use can be limited to some. See also Connection permissions.
Writing is granted for a whole type
create, update and delete, and read on connections, are always granted for every resource of a type. Their Scope must select the whole type, such as mission-control-builtin/all-configs. You can let someone edit all playbooks, but not only some of them.
Combining Roles
A person can be bound to several Roles, and they can do everything any of them allows. Order doesn't matter, and adding a Role never takes access away.
So you can build up access from small Roles. Instead of one large "platform engineer" Role, you might bind three: viewer, a Role to run the monitoring playbooks, and a Role to use the alerting connections.
To take access away from someone regardless of what else they hold, use a deny RoleBinding.
Tips
- Start from the shipped Roles.
vieweralready reads the catalog, topology, health checks, playbooks and views. Bind it first, then write Roles only for what people need beyond that. See Shipped Roles. - Name rules after what they allow, such as
read-stagingorrun-monitoring. The names appear in status messages and on the Subjects page. - Keep Roles small. Since one broken rule stops the whole Role, small Roles limit the damage.
- Reference Scopes in other namespaces with
namespace/name, such asmission-control-builtin/all-configs. A bare name refers to a Scope in the Role's own namespace.
Next steps
Give your Role to someone with a RoleBinding.