Scopes and permissions
Connector credentials tells you which keys to create. This page tells you with what permissions to create them.
Datadog
Section titled “Datadog”Two credentials with different natures: the API key has no scopes, the Application key does.
Application key: scopes
Section titled “Application key: scopes”| Scope | Enables | Required? |
|---|---|---|
| none | metrics.* |
Datadog exposes no metrics-read scope: a standard app key already reads metrics. |
logs_read_data + logs_read_index_data |
logs.* |
If you use logs |
apm_read |
traces.*, topology.* (Software Catalog) |
If you use APM |
monitors_read |
monitors.* |
Always (see below) |
incident_read |
incidents | If you use incidents |
rum_apps_read |
rum.* |
If you use RUM |
synthetics_read |
synthetics.* |
If Datadog serves synthetics |
api_keys_read |
only key hygiene (security) |
Optional, and sensitive |
API key
Section titled “API key”No scopes. It is only used at /api/v1/validate, in the health check.
DD_SITE
Section titled “DD_SITE”Not a permission, but it fails alongside one: if your organization is EU or US3/US5/AP1, a valid key
returns 403 against the wrong site. Check this before investigating scopes.
Grafana Cloud
Section titled “Grafana Cloud”Access Policy scopes. Only the first two are required.
| Scope | Enables | Required? |
|---|---|---|
metrics:read |
Mimir: metrics.*, service graph, Faro and Synthetics metrics |
Yes |
logs:read |
Loki: logs.* |
No; degrades per operation |
traces:read |
Tempo: traces.* |
No; degrades per operation |
alerts:read + rules:read |
Alerting: monitors.* |
No |
stacks:read + stack-dashboards:read |
dashboard search in the stack | No |
| Synthetic Monitoring | synthetics.* via /api/v1/check/list |
Scope name to confirm in your account |
Mimir is the only required one because it’s the backbone: without it the connector has nothing to serve. The others degrade with an actionable error, not a boot failure.
A read-only IAM policy, served as a file so you don’t have to copy it off the screen:
curl -O https://docs.rootpilot.sh/aws/rootpilot-edge-readonly.json
aws iam create-policy \ --policy-name RootPilotEdgeReadOnly \ --policy-document file://rootpilot-edge-readonly.jsonIt covers the whole catalog. If you don’t use a capability, drop the matching Statement before
creating it. That’s what the Sids are for: each one is a removable domain.
The file is canonical; the block below is the same content, and a build check fails if the two drift apart.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "IdentidadeEContexto", "Effect": "Allow", "Action": [ "sts:GetCallerIdentity", "organizations:DescribeOrganization", "organizations:ListAccounts" ], "Resource": "*" }, { "Sid": "CustoEFinOps", "Effect": "Allow", "Action": [ "ce:GetCostAndUsage", "ce:GetAnomalies", "ce:GetReservationCoverage", "ce:GetReservationPurchaseRecommendation", "ce:GetSavingsPlansCoverage", "ce:GetSavingsPlansPurchaseRecommendation", "compute-optimizer:GetEnrollmentStatus", "compute-optimizer:GetEC2InstanceRecommendations", "compute-optimizer:GetEBSVolumeRecommendations", "compute-optimizer:GetLambdaFunctionRecommendations", "compute-optimizer:GetAutoScalingGroupRecommendations", "compute-optimizer:GetRDSDatabaseRecommendations" ], "Resource": "*" }, { "Sid": "Compute", "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "ec2:DescribeAddresses", "ec2:DescribeNetworkInterfaces", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeVpcs", "ec2:DescribeVpcEndpoints", "ec2:DescribeVpcPeeringConnections", "ec2:DescribeRouteTables", "ec2:DescribeInternetGateways", "ec2:DescribeNatGateways", "ec2:DescribeCustomerGateways", "ec2:DescribeVpnConnections", "ec2:DescribeVpnGateways", "ec2:DescribeTransitGateways", "ec2:DescribeTransitGatewayAttachments", "ec2:DescribeManagedPrefixLists", "ecs:ListClusters", "eks:ListClusters", "eks:DescribeCluster", "eks:ListNodegroups", "eks:DescribeNodegroup", "ecr:DescribeRepositories", "ecr:DescribeImages", "lambda:ListFunctions", "lambda:GetFunctionConfiguration", "lambda:ListEventSourceMappings" ], "Resource": "*" }, { "Sid": "BancosDeDados", "Effect": "Allow", "Action": [ "rds:DescribeDBInstances", "rds:DescribeDBClusters", "rds:DescribeDBSubnetGroups", "rds:ListTagsForResource", "dms:DescribeReplicationInstances", "dms:DescribeReplicationTasks", "es:ListDomainNames", "es:DescribeDomains", "elasticache:DescribeCacheClusters", "elasticache:DescribeReplicationGroups", "pi:DescribeDimensionKeys", "pi:GetResourceMetrics" ], "Resource": "*" }, { "Sid": "Rede", "Effect": "Allow", "Action": [ "elasticloadbalancing:DescribeLoadBalancers", "elasticloadbalancing:DescribeTargetGroups", "elasticloadbalancing:DescribeTargetHealth", "elasticloadbalancing:DescribeInstanceHealth", "elasticloadbalancing:DescribeTags", "route53:ListHostedZones", "route53:GetHostedZone", "route53:ListResourceRecordSets", "sqs:ListQueues", "sqs:GetQueueUrl", "sqs:GetQueueAttributes" ], "Resource": "*" }, { "Sid": "Observabilidade", "Effect": "Allow", "Action": [ "cloudwatch:GetMetricData", "cloudwatch:DescribeAlarms", "logs:DescribeLogGroups", "logs:FilterLogEvents", "logs:StartQuery", "logs:GetQueryResults", "application-signals:ListServices", "application-signals:ListServiceLevelObjectives", "application-signals:BatchGetServiceLevelObjectiveBudgetReport" ], "Resource": "*" }, { "Sid": "AuditoriaEConsulta", "Effect": "Allow", "Action": [ "cloudtrail:LookupEvents", "cloudtrail:ListEventDataStores", "cloudtrail:StartQuery", "cloudtrail:DescribeQuery", "cloudtrail:GetQueryResults", "athena:StartQueryExecution", "athena:StopQueryExecution", "athena:GetQueryExecution", "athena:BatchGetQueryExecution", "athena:GetQueryResults", "athena:ListQueryExecutions" ], "Resource": "*" }, { "Sid": "SegurancaIamSomenteLeitura", "Effect": "Allow", "Action": ["iam:ListUsers", "iam:ListAccessKeys", "iam:GetAccessKeyLastUsed"], "Resource": "*" } ]}Five traps in this policy
Section titled “Five traps in this policy”1. athena:StartQueryExecution and cloudtrail:StartQuery look like writes, and aren’t. That is
how you run a read query in those services: the action creates an execution, it doesn’t change
data. The handler validates the SQL first: DDL and DML are rejected. If your security review reflexively
blocks Start*, that’s the argument.
2. Athena needs S3, and it isn’t above. Results go to a bucket, and without
s3:GetObject/s3:PutObject/s3:ListBucket on the results bucket, the query fails. Depending on
your catalog, also glue:GetTable/GetDatabase/GetPartitions. Left out deliberately: the ARN is
yours, and a wildcard here would be worse than an explicit omission.
3. DocumentDB uses the rds: namespace. There is no docdb:, and looking for it without finding it
leads to over-granting.
4. Three prefixes don’t match the service name: CloudWatch Logs is logs:, Cost Explorer is ce:,
OpenSearch is es:.
5. No QuickSight embed. The connector has code for GenerateEmbedUrlFor* and
GetDashboardEmbedUrl, but those four operations were deferred by posture and are not in the
catalog: enforcement refuses them before they reach the connector. Granting those permissions would
be privilege nothing can exercise. If you use the bi capability, add only the
quicksight:Describe*, List*, and Search* actions.
Two different AWS credentials: don’t conflate them
Section titled “Two different AWS credentials: don’t conflate them”| For what | How | Permission |
|---|---|---|
| Reading your sources | RUNNER_AWS_CRED_MODE: static (keys) or chain (IRSA, instance profile) |
The policy above |
| Reading your secrets | RUNNER_SECRET_STORE_MODE=aws |
secretsmanager:GetSecretValue, scoped to your secret prefix |
The second should be tightened to the ARN, not *:
{ "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:REGION:ACCOUNT:secret:rootpilot/*"}The sso:GetRoleCredentials and sso:ListAccountRoles you’ll find in the code belong to the
development path with aws sso login. Don’t grant them in production.
Kubernetes (EKS)
Section titled “Kubernetes (EKS)”Permissions here come in two layers, and granting only the first is the most common mistake.
Layer 1, IAM: eks:DescribeCluster (already in the policy above), to discover the cluster’s
endpoint and CA.
Layer 2, RBAC inside the cluster. The connector mints a token the way aws eks get-token does and
talks to the API server. An IAM identity with no binding in the cluster gets 401/403 from
Kubernetes, not from AWS, and the error doesn’t mention IAM, which sends the investigation the wrong
way.
The connector reads only this:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: rootpilot-readrules: - apiGroups: [""] resources: ["pods", "services", "endpoints"] verbs: ["get", "list"] - apiGroups: ["apps"] resources: ["deployments"] verbs: ["get"] - apiGroups: ["networking.k8s.io"] resources: ["ingresses"] verbs: ["get", "list"]Bind it to the runner’s IAM identity with an EKS access entry (or an aws-auth entry on older
clusters) plus a ClusterRoleBinding.
Without EKS_CLUSTER in the environment, the health check answers skipped: no cluster configured,
which is not a permission error, but looks like one.
Project-level roles. roles/viewer covers most of it; these are the ones usually missing:
| Role | Enables |
|---|---|
roles/viewer |
Compute, GKE, Cloud SQL, Run, Functions, DNS, Artifact Registry, Firestore, Redis |
roles/monitoring.viewer |
metrics.* |
roles/logging.viewer |
logs.* |
roles/cloudasset.viewer |
asset inventory |
roles/recommender.viewer |
recommendations |
roles/securitycenter.findingsViewer |
security.* |
roles/billing.viewer on the billing account |
cost. Note it’s on the billing account, not the project |
roles/bigquery.dataViewer + roles/bigquery.jobUser |
reading GCP_BILLING_EXPORT_TABLE |
roles/iam.securityReviewer |
IAM reads |
Cost is the fiddly one: it needs the role on the billing account and BigQuery access to the
export table. Without GCP_BILLING_EXPORT_TABLE, the operation fails explicitly.
With RUNNER_GCP_CRED_MODE=chain, these roles go on the Workload Identity GSA and no key circulates.
GitHub
Section titled “GitHub”Fine-grained token (recommended):
| Permission | Level | For what |
|---|---|---|
| Metadata | Read | Required. The base for all the others |
| Contents | Read | Commits, content, refs, tags, comparison, code search |
| Pull requests | Read | PRs, PR files, PRs associated with a commit |
| Actions | Read | Workflow runs |
| Checks | Read | Checks for a ref |
| Administration | Read | Only branch protection (see below) |
| Dependabot alerts | Read | Security alerts |
Classic token, if you prefer: repo + read:org + security_events.
None of these permissions are writes. The connector contains not a single mutating call. See Security model.
Bot token scopes:
| Scope | For what |
|---|---|
channels:read, groups:read |
listing conversations |
channels:history, groups:history |
reading history |
chat:write |
chat.postMessage, write operation |
channels:manage |
chat.createChannel, write operation |
The remaining platforms
Section titled “The remaining platforms”| Platform | What the credential needs | Notes |
|---|---|---|
| Checkly | Read access to checks and results (/checks, /check-results/{id}) |
CHECKLY_ACCOUNT_ID is required in practice for a user token; without it, an unexplained 401 |
| Vercel | Read access to deployments and projects (/v6/deployments, /v9/projects) |
Read-only token scoped to the team; VERCEL_TEAM_ID required for a team token, otherwise a silent 403 |
| Amplitude | API key + secret with access to the Dashboard/Export APIs | Covers /events/segmentation, /funnels, /dashboards, /charts. Reads |
| SonarQube | User token with Browse on the analyzed projects | /api/issues/search, /api/measures/component, /api/qualitygates/project_status, /api/hotspots/search |
| Jira | Browse projects, plus Create issues, Add comments, and Transition issues | The catalog’s only three ticket writes. Without them, reads still work |
| Cloudflare | Zone:Read + DNS:Read |
Scope it to the zones you need, not the whole account |
| Azion | Token with read access to workspace (applications, connectors, workloads) and the metrics and events GraphQL APIs | The connector’s POSTs are GraphQL query transport, not mutations |
| MongoDB | User with read on the queried databases |
MONGO_URI embeds the credential; use a dedicated read-only user |
Regenerating this list
Section titled “Regenerating this list”Before an audit, check against the tree rather than trusting the page:
# AWS commands actually used, grouped by servicegrep -rhoE "\bnew [A-Z][A-Za-z0-9]+Command\b" packages/connectors/src | sort -u
# A connector's REST endpointsgrep -ohE "'/[a-zA-Z0-9/_.{}-]+'" packages/connectors/src/sonarqube.ts | sort -u
# What's actually exposed (enforcement refuses the rest)grep -oE "'[a-z]+\.[a-zA-Z]+'" apps/runner/src/connectors/manifests/real.ts | sort -uThat last line is the one that matters for least privilege: permission for an operation that isn’t in the catalog is privilege nothing can exercise. That’s how the QuickSight embed actions came off this page.