Skip to main content
Dorgu’s central claim is that the operator never writes your workloads. This page is the proof rather than the assertion: the operator’s full ClusterRole, and an explicit list of what it cannot do. Read it alongside the ownership model. Together they are the whole safety story:
  • The operator cannot write workloads. It has no create, update, or patch on Deployments. Kubernetes enforces that, not Dorgu’s code.
  • The CLI will not write workloads you have not handed it. It runs as you, so RBAC cannot stop it. What stops it is the ownership guard: it refuses to patch a Deployment that Helm, ArgoCD, or Flux reconciles. Those three stamp what they reconcile, so their ownership is detectable; kustomize is not, and the docs say so rather than implying a protection Dorgu cannot deliver.
One is enforced by the API server. The other is enforced by the CLI. Both are needed, because they defend against different things.

What the operator cannot do

These are the permissions the ClusterRole does not grant. Every one of them fails with a 403 Forbidden from the API server if the operator ever tried.
Verify it yourself on any cluster. kubectl auth can-i answers as the operator’s service account:
Adjust the namespace and name if you installed with a different release name or serviceAccount.name. The full list is kubectl auth can-i --list --as="$SA".

What the operator can do

Everything the operator is permitted to do falls into three buckets: read cluster state, write its own CRDs, and record events. Note the shape of it. Every write verb in that table targets a dorgu.io CRD, a Kubernetes Event, or a leader-election Lease. Nothing else in the cluster is writable.

The full ClusterRole

This is charts/dorgu-operator/templates/rbac.yaml as it ships, rendered with argocd.enabled=true so the optional rule is visible. Read your own cluster’s copy with kubectl get clusterrole dorgu-operator-manager -o yaml.
The apps rule is the one to read twice. verbs: ["get", "list", "watch"] on deployments is what makes “the operator never writes workloads” a fact about your API server rather than a promise about Dorgu’s source code. Even a bug, a malicious plan from the AI planner, or a compromised operator pod cannot patch a Deployment through this service account.

Why the operator can delete DorguEvents

This ClusterRole is a trust artifact, so a new verb in it deserves an explanation rather than a silent diff. delete on dorguevents.dorgu.io is new in operator v0.10.0. Without it the retention cleaner had no permission to act: it ran every cycle, failed, logged the failure, and removed nothing, while DorguEvent records accumulated in etcd. A five-app clean-room cluster produced 187 records in 100 minutes with no bound on the count. See dorguEvents.retention and dorguEvents.maxRecords. The grant is scoped to Dorgu’s own CRD and to no workload kind, so “the operator never writes workloads” is unchanged. Two tests in the operator repo hold that line, one asserting the new verb and one asserting the operator still cannot create, update, patch, or delete Deployments, ReplicaSets, Pods, Nodes, or Namespaces. Check both halves on your own cluster rather than taking either on trust:

How the AI API key reaches the operator

The operator has no permission to read Secrets, which raises a fair question: how does it get your Anthropic key? The key is injected as an environment variable via secretKeyRef in the pod spec. The kubelet reads the Secret and materialises it into the container’s environment; the operator’s ServiceAccount is never involved and never needs get secrets.
The practical consequences:
  • The operator can read exactly one secret value, the one you explicitly wired into its pod spec. It cannot enumerate or read any other Secret in the cluster.
  • Rotating the key means updating the Secret and restarting the pod. There is no live re-read, because there is no read.
  • In production, create the Secret out of band and set llm.existingSecret. Passing llm.apiKey through Helm values means the raw key stays visible to helm get values and helm template.
AI is opt-in, and it sends cluster context to Anthropic. Detection and rule-based diagnosis are local and free, so they are on by default. AI diagnosis and AI-written plans require llm.provider, a key, and aiRemediation.enabled set explicitly. A default install spends nothing on inference and sends nothing anywhere. See AI setup for exactly what is included in a prompt.

What the CLI can do, and with whose credentials

The CLI is the other half of the story, and its permissions are not Dorgu’s to grant. dorgu shells out to kubectl using your kubeconfig and your current context. It has exactly the access you have. If you can patch a Deployment, so can it; RBAC will never stop the CLI on your behalf. That is why the ownership guard is in the CLI rather than in RBAC. It is not a permission boundary, it is a judgment about whether writing is correct:
The ownership guard is enforced by CLI v0.10.0 or newer. Operator v0.9.0 supplies the facts in spec.workloadRef and strips workload-writing commands, but refusing the patch is the CLI’s job. An older CLI against a v0.9.0 operator will still patch an owned Deployment. See version coupling.

Guardrails on the remediation path

RBAC bounds what is possible. These bound what Dorgu will propose, and they run before any RemediationAction is created.
trustLevel, policies.selfHealing.enabled, and approval.autoApproveRule are descriptive, not enforced. trustLevel is only passed to the AI planner as context, detection and proposal run regardless of enabled (use mode: observe to stop at diagnosis), and no controller reads autoApproveRule. Every remediation requires human approval. maxRemediationsPerHour and excludeNamespaces are enforced. We would rather you knew.

Hardening notes

Next steps

Ownership model

Why Dorgu refuses to patch a Helm or ArgoCD-owned workload

Trust model

The progressive trust levels and what is actually implemented

Helm values

Every chart option, including webhook mode and leader election

AI setup

Key handling, what is sent to Anthropic, and how to turn it off