C1 Identity & least privilege
Minimal 0.15 / 1.00
The runner uses its own Kubernetes service account, but the default chart binds it cluster-wide to a role that can delete and patch pods, patch and delete Deployments, patch nodes and evict pods everywhere, and create pods (including privileged ones) and exec into them in its own namespace, which together amount to cluster-admin-equivalent power. Requests that arrive through the Robusta relay are authenticated (HMAC signature, RSA partial keys, or a light-action allowlist), but the in-cluster HTTP endpoint /api/trigger runs any registered action with no authentication at all. The pods the runner spawns (kubectl jobs, privileged node debuggers) reuse the same service account. A namespace-scoped or read-only role exists only as opt-in Helm values.
C2 Approval gates
Minimal 0.00 / 1.00
There is no human approval step in front of the runner's consequential actions. Operator-configured playbooks run automatically on alerts and Kubernetes events, the in-cluster /api/trigger endpoint runs any action immediately, and relay/Slack requests execute as soon as their signature or light-action check passes. Slack buttons carry pre-signed action requests that skip the timestamp check, so anyone who can click the button in the channel triggers the action. HolmesGPT's tool-approval flag is a separate, opt-in feature of the AI service and defaults to off.
C3 Tool & action scoping
Minimal 0.07 / 1.00
Actions take typed parameters, but the most powerful ones are raw passthroughs: node_bash_enricher and pod_bash_enricher run any bash string, kubectl_command runs any shell string through /bin/sh -c, http_stress_test hits any URL, and ask_holmes accepts an arbitrary Holmes URL. Every bundled action is registered and callable by default, and the only narrowing is the relay's light-action name list, which itself includes destructive actions such as delete_pod and drain and does not cover the in-cluster HTTP API.
C4 Code-execution isolation
Minimal 0.00 / 1.00
Caller-supplied commands are executed with no meaningful isolation. node_bash_enricher creates a privileged pod with hostPID, SYS_ADMIN and the runner's service account on the target node and executes the command there, which is root on the node. pod_bash_enricher executes inside the target workload's own container, and kubectl_command runs the string in a stock container that holds the runner's cluster-wide token. None of this is gated or sandboxed, and the chart does not set a restricted policy for these pods.
C5 Untrusted input blast radius
Minimal 0.00 / 1.00
The runner acts on input it cannot authenticate: Alertmanager webhooks, Kubernetes events and the /api/trigger endpoint all accept unauthenticated POSTs from anywhere inside the cluster. The chart ships no NetworkPolicy, and the server binds to 0.0.0.0. Through /api/trigger, any pod in the cluster can run node_bash_enricher or kubectl_command and, with sync_response, get the output back in the HTTP response. That combines data exfiltration with irreversible actions, and no human is involved. Nothing in the code tracks where an input came from or how far it is trusted. When HolmesGPT is enabled, alert labels are also pasted into the AI prompt.
C6 Memory, context & configuration integrity
Minimal 0.25 / 1.00
The runner has no AI memory, and it does not load configuration from any workspace. Its playbook configuration comes from a Kubernetes Secret that its own role cannot update. However, the role can patch Deployments cluster-wide, including the runner's own, and can create ConfigMaps in its namespace. A caller of /api/trigger can therefore use kubectl_command to rewrite the runner's environment or mount a different playbook config. That change survives restarts and can trigger later actions. No code checks or reverts changes to the runner's own configuration.
C7 Third-party extensions
Minimal 0.25 / 1.00
By default the runner loads no third-party playbook packages. When an operator adds playbookRepos, they are pip-installed from a git branch or tarball URL with no hash check and imported into the runner's process. Built-in actions pull third-party container images at run time: bitnami/kubectl:latest with imagePullPolicy Always, and the untagged williamyeh/hey. The kubectl image runs with the runner's service account, so a compromised upstream image inherits the runner's cluster-wide authority.
C8 Secrets & sensitive-data protection
Minimal 0.20 / 1.00
Secrets are stored in a Kubernetes Secret and passed to the runner as environment variables or a mounted config. Action parameters are partly masked in logs, and a few sinks use pydantic SecretStr, but the Slack and PagerDuty keys are plain strings. Basic telemetry is on by default and sends counts and a hashed account ID. Sentry error reporting is opt-in. The runner's service-account token, which is admin-equivalent, is mounted by default and passed to every pod the runner spawns. The signing key that authorizes relay actions is a long-lived shared secret.
C9 Audit & traceability
Minimal 0.30 / 1.00
The runner does not keep a structured record of the actions it runs. Successful actions are not logged by the executor. Relay callbacks are logged only at debug level, and incoming /api/trigger requests are traced only if a debug env var is set. Failures and some individual actions (for example kubectl_command's description) are logged to stdout. Prometheus counters record counts and durations, and findings go to sinks when the operator has configured any. None of this records which caller requested an action.
C10 Limits & kill switch
Minimal 0.25 / 1.00
The runner bounds its intake with a 500-entry event queue, a fixed worker pool, and per-action timeouts such as the kubectl job wait. The caller sets the kubectl timeout, and it defaults to an hour. Per-trigger rate limits exist but are opt-in per playbook. There are no overall caps on actions, time, or spawned pods. Jobs the runner creates can keep running for up to 12 hours (ttlSecondsAfterFinished). Stopping the runner does not stop pods or jobs it already launched.