v0.13.0 (Helm chart 0.13.0) and CLI v0.14.0 are one release in two parts. They add the approval-time re-check and the apply ticket, and the two halves have to be upgraded together, in a fixed order.
At a glance
A CLI
v0.14.0 against an older operator is safe but limited. approve records the decision, waits up to 30 seconds, finds no ticket, patches nothing, and says so. An older operator still patches the ApplicationPersona when it sees the approval, so the persona changes while the CLI refuses to patch the workload, and the two disagree until you upgrade the operator (or revert the persona by hand). If you must stay on an older operator, stay on an older CLI.
Before you upgrade
-
Decide the proposals that are waiting, or let them go. An action created by an older operator carries no operator-written proposal record, so after the upgrade it cannot be approved. Approving one is refused (
NoProposalRecord, CLI exit4) and the action endsExpiredwith the condition reasonPredatesGate. That is terminal and starts no cooldown, and the operator proposes again on the next cycle. Approve or reject what isPendingnow with your current CLI if you want a say first. You can also delete the old ones (kubectl delete remediationactions -A --all, or only thePendingones) or leave them to expire. -
Let in-flight remediations finish. An action that is
ApplyingorVerifyingwhen the operator upgrades has no ticket. It completes normally, but a rollback of one is refused and endsFailedwith a message that the change is still applied. -
Check your values files for
websocket.*(ignored, harmless) and for anything you set ondorguEvents.*(see the event pipeline). -
Check who manages the operator’s role. If your GitOps tooling pins the ClusterRole instead of taking it from the chart, add the new
deleterule onincidentmemories(see new permission).
Step 1: upgrade the CLI
Installv0.14.0 using the installation page, then check it:
Step 2: apply the CRDs
Helm installs CRDs once and never upgrades them. Pull the chart and apply itscrds/ directory yourself:
status.gate and status.applyTicket on RemediationAction, the Ticketed phase, the self-resolved incident outcome, and a DNS-1123 label pattern (63 characters at most) on the ClusterPersona’s excludeNamespaces items. An existing excludeNamespaces entry such as Shop or * never matched anything, and the object is rejected on its next update until you fix the entry.
Step 3: upgrade the operator
--reuse-values also works from 0.12.x. Helm’s --reuse-values ignores the new chart’s defaults, so chart 0.13.0 reads every key added since 0.12.1 (healthCheck.readinessThreshold, healthCheck.livenessRestarts.*, healthCheck.recurrence.*, healthCheck.resolvedRetention, remediation.pendingTTL, eventPipeline.enabled, llm.cacheTTL) with a built-in default. --reset-then-reuse-values (Helm 3.14 or newer) keeps your values and picks up the new defaults from the chart, which is why this guide uses it. Either way, apply the CRDs first.Step 4: verify
can-i must still say no. dorgu report should print Operator: v0.13.0.
What breaks
remediation reject --reason takes a fixed list
--reason is now one of wrong-diagnosis, too-risky, not-now, duplicate, not-needed, other. Free text is an error that lists the valid values. Put the sentence in the new --note flag. --reason other requires one. Leaving --reason out still rejects.
RejectionReason condition. See remediation reject.
approve and heal work differently
approvewaits for the ticket. It records the approval, waits up to 30 seconds for the operator’s apply ticket, and patches exactly what the ticket says. A script that parsed the old output ofapprove, or that gave it a shorter timeout, needs a look.- A refusal by the operator’s gate exits
4and prints the reason. Exit4already means “declined by design” for owned workloads. A wrapper that treats every non-zero exit as breakage will misreport it. - No ticket exits
1and patches nothing: the operator is older thanv0.13.0, the ticket did not arrive in 30 seconds, or it expired. approvereports “approved” only if the approval stands. If the operator expired or superseded the action in the moment between the CLI reading it and writing the approval, the CLI says what it became and exits non-zero.healno longer doubles as an approval. Healing aPendingremediation is refused: approve it first. Afterapprove --no-heal,healworks only while the operator still has the actionApplying, which is its 10 minute verification window.- The CLI re-detects ownership from the live Deployment before every patch, and refuses one that carries Helm, Argo CD or Flux stamps, kustomize build metadata, a controller owner reference or a GitOps field manager, whatever the proposal says.
- Every decline writes a Warning Event on the RemediationAction (
DorguCLIDeclined,DorguCLINoTicket,DorguCLITicketExpired,DorguCLITicketMismatch), so the CLI needs permission to create Events in the remediation’s namespace. A protected kube context writes nothing.
dorgu remediation.
The WebSocket server is removed
--enable-websocket and --websocket-addr are gone: passing either now stops the operator at startup with flag provided but not defined. The Helm values websocket.enabled, websocket.port, websocket.service.type and websocket.service.port, the <release>-websocket Service and the container port are gone too. The server was unauthenticated and nothing used it.
Old values files still install: Helm ignores unknown keys, so --set websocket.enabled=true is a harmless no-op. If you ran kubectl port-forward svc/<release>-websocket, it has nothing to forward to. Follow changes with kubectl get incidentmemories,remediationactions -A -w.
What is deprecated
spec.action on a RemediationAction is a notification only. spec.steps[] is the only plan. The operator now writes spec.action as {type: notification} with no patch and no prePatchState, and applies status.applyTicket, never spec.action. An edit to the spec after the operator proposed the action refuses the approval. Anything that read spec.action.patch should read spec.steps[], or status.gate, which records what the gate read and decided. spec.workloadRef, spec.steps[].safety and spec.action.prePatchState are still written for one more release; where they disagree with status.gate, trust status.gate.
New phases and records
The full set of reasons and where each is recorded is in How Dorgu decides.
Behavior that changes without a flag
- Approval goes through the gate again, on the live cluster. A cluster that went to
observe, a namespace that became protected, a workload that was replaced, or an edited spec each refuse the approval. This is the intended change. - A recovery with nothing patched is
self-resolved, notresolved. - Rollback never overwrites a later edit, and a rollback that restored the persona but not the live Deployment is
partial. - New incidents will appear. Faults that raised nothing before now do: see the new signals.
- One OOM is one incident, not a
CrashLoopBackOffand anOOMKilled. An existing openCrashLoopBackOffincident for an OOM-ing pod resolves through the normal grace period. - A fault that comes back opens a new incident, named
<first-name>-r<N>, instead of folding silently into the resolved one. - A stale
OOMKilledis no longer a live signal. A container that was OOM-killed and has been Running and Ready for 2 minutes (or twice the health-check interval, if longer) stops raising it. - Pod detection skips the same namespaces the gate refuses. It used to skip only
kube-system. It now also skips the operator’s namespace,safety.excludedNamespacesand every ClusterPersonaexcludeNamespacesentry. A pod failing there raises no incident at all. If the ClusterPersona cannot be read, the pod collector fails instead of guessing. - The model is asked once per changed situation, and its replies are validated. See Detection and diagnosis.
--llm-cache-ttl=0restores a call every cycle. - More paths fail closed. An unreadable rate limit refuses proposals until it recovers. With
POD_NAMESPACEunset the gate refuses every proposal and AI is off. The chart always sets it. Set it yourself when you run the operator outside a cluster. - A crash loop, a not-Ready container or a liveness kill still gets an advisory proposal. The AI planner’s plan for these often mixes a memory raise with advice, and the gate refuses a persona change the rules did not recommend (
Validate/action-class). The persona-update steps are dropped and the plan’s advisory steps are proposed as a notification. The refusal of the first plan is still recorded (aDorguValidateRefusedEvent on the incident and a gate-decision count). Sentences in the model’s text that name a value of the dropped change are removed, and the explanation says the change was not proposed. A plan with nothing advisory in it stays refused. - One bad image is one incident and one proposal. A pod stuck on
ImagePullBackOff,ErrImagePullorInvalidImageNameno longer also raisesPodPendingLongfive minutes in. If the two still meet in one cycle, the pending diagnosis folds into the image-error incident while that image error is current. - A container that exits right after it starts is detected in about a minute, not after five restarts.
RestartLoopandLivenessRestartsalso read a container between runs, from its current termination, and a pod younger than the restart window counts its restarts as the delta. CrashLoopBackOff still owns the container in a cycle in which it fired. - The leader lease is released when the manager stops, so a rolling update no longer leaves detection without a leader for about 30 seconds.
- Cluster-scoped groups (nodes, control plane) are no longer sent to the model, and a group the planner would never act on is diagnosed by the rules alone.
The event pipeline is off by default
The Kubernetes-Event watcher and theDorguEvent cleaner no longer start, and the health cycle no longer writes a DorguEvent per signal, because nothing reads those records. Turn it back on with eventPipeline.enabled=true (--enable-event-pipeline). dorguEvents.retention and dorguEvents.maxRecords apply only then. A 0.12.x install had the pipeline on implicitly. After the upgrade it is off, so no DorguEvent is written or pruned, and the dorguEvents.retention and dorguEvents.maxRecords values you set (including ones carried over by --reuse-values) do nothing until you set eventPipeline.enabled=true. Existing DorguEvent records are not deleted, and with the pipeline off nothing prunes them:
kubectl describe shows) are unchanged and always on.
New health signals
ReadinessNotReady, LivenessRestarts and RestartLoop are new, so workloads that were quietly unhealthy get an incident for the first time. An app that legitimately needs longer than 2 minutes to become Ready should have a startupProbe (a container whose startup probe has not passed is never reported) or a longer healthCheck.readinessThreshold. See Detect.
New flags and Helm values
The full list is on the Helm values and configuration pages.
New permission
The operator’s ClusterRole gainsdelete on incidentmemories.dorgu.io, and nothing else. It is how the operator prunes resolved incidents past healthCheck.resolvedRetention: Dorgu’s own records, never a workload. If you manage the role yourself and skip this rule, every prune fails with a Forbidden error and nothing else changes. See security and permissions.
New metrics
Every refusal, ticket, verification, rollback, heal credit and model request is now a metric, pre-registered at zero. The complete list is in the metrics reference.Known gaps at this release
- A client with write access to
remediationactions/statuscan forge an approval history together with a ticket. What it can make the operator apply is limited to what the spec’s own plan produces, and the operator never writes workloads, but the CLI trusts a ticket it finds in status. The CLI carries stopgap checks. An admission policy that makes the operator the only writer is planned. Until then, treatpatchonremediationactions/statusas the right to approve. - Approver identity is not enforced.
approvedByis free text, and nothing requires that the approver is not the proposer. Identity checks are planned for a later release. - A rejection cooldown can be released by editing two annotations on an action or an incident. It only lets Dorgu create one more
Pendingproposal, which still needs a human. - CPU throttling is not detected. The operator reads no cAdvisor or Prometheus data.
- A liveness kill that exits
0is detected only through kubelet events, which Kubernetes keeps for about an hour. - A client-side
kubectl applyfrom CI reads as unmanaged, so Dorgu may propose to patch a Deployment your pipeline owns. - An HPA scaling a Deployment during verification reads as divergence, so the heal is
partialand not credited. - A cached AI summary can be up to
llm.cacheTTLold.