diff --git a/cluster b/gitops-repo @@ observed -> proposed @@
yalso
+

+ It works out why a workload is failing, then opens a pull request.

Yet another LLM SRE operator. It watches a Kubernetes cluster, groups the symptoms of one failure together, finds the manifest in your GitOps repository that produced the object, and proposes a minimal patch with the evidence it rests on. It never changes the cluster.

Reading now 282 observations
41 workloads / 14 namespaces
32 incidents, 9 advised
1 pull request
The premise why a pull request
§1

An operator that edits your cluster to fix it creates two problems. The change is invisible to the repository that is supposed to describe the cluster, and the next reconcile undoes it. YALSO does not do that. Every fix it proposes arrives the way any other change does: as a pull request that a human reads and merges.

The consequence is that a wrong answer costs a review, not an outage. That trade is the whole design, and the rest of this page is what follows from it.

The alternative kubectl patch deploy/api
  --patch '{"spec":…}'
→ reverted by reconcile
→ absent from git history
One sweep observe → publish
§2

Nine steps, each producing something the next one reads. The order is the operator's, not a diagram of it.

01Observe pods, workload controllers and events, every visible namespace
02Detect crash-loop-back-off · oom-killed · probe-failure · job-failure · not-starting · pod-failed · controller-failure
03Deduplicate fingerprint → one record however many sightings
04Correlate OwnerTopology · SourceLocality · DependencyEdge · ImageIdentity
05Resolve the source which application owns it, which file declares it
06Analyse evidence + manifest → root cause, one change
07Gate citation · verification · minimality — any failure refuses
08Publish one branch, one pull request, the evidence and what is not claimed
09Learn merged, closed, or amended by a human before merging
The three gates refuse, not warn
gate-01

Citation

Every statement in the analysis must cite a piece of evidence that was actually collected, and every hunk must cite evidence that reaches the field it changes. A finding that cites nothing is not a finding.

- the container is probably short of memory cites: (none) + REFUSED: citation gate
gate-02

Verification

A second model call is shown the analysis and the same evidence and asked whether it holds. Agreement is required, and it is unanimous across every judgement in the reply: one disagreement refuses the analysis.

judgements: 4 - agreed: 3 of 4 + REFUSED: verification gate
gate-03

Minimality

The patch is bounded in hunks and in changed lines, and it may not repeat a change to a field it has already touched. A fix that rewrites a file is not a fix.

- 6 hunks, 84 changed lines - resources.limits.memory touched twice + REFUSED: minimality gate
§3

A language model will produce a confident answer whether or not it has grounds for one. These exist because that is the failure mode. A refusal is recorded on the incident with its reason, so a gate that fires is visible rather than silent.

Recorded refusals AnalystDeclined
UnparseableAnalysis
RulesOverCeiling
PromptOverCeiling
WriteAccessRefused
HelmSourceUnsupported
SourceNotFound
What it cannot do enforced, not documented
§4

The reason to run this at all is that its blast radius is small, so each boundary below names the mechanism rather than an intention.

BoundaryHow it is enforced
Change any object in the cluster except its own records The ClusterRole grants no write verb outside its own API group, and the operator's own client refuses one regardless. A bug that tried would be stopped twice.
Read a credential it was not pointed at Secrets are read from the operator's own namespace, by name and key, named in a custom resource. The Helm chart never creates a Secret and refuses a values file that looks like it carries one.
Send a credential to the model Manifests, pod status, events and container logs are redacted before they leave: credential-shaped values, the data of every Secret, and credential-shaped environment variables. Redaction is pattern based and is not a guarantee, which is why the model endpoint should be one you control.
Open a pull request against a repository you did not list A resolved repository URL outside the configured list is refused outright.
Install helm
§5
+
helm install yalso ./charts/yalso \
  --namespace yalso-system --create-namespace \
  --set api.enabled=true

Then apply a YalsoConfig named default naming your model endpoint and the repositories it may propose against, and read the status conditions. The chart refuses a values file that oversteps: a key shaped like a credential, a key that belongs in a custom resource, and a replica count are each rejected at render time with the reason.

helm test yalso -n yalso-system --logs
What the tests reach the health endpoint
the read API over the cluster network

A chart test that passes proves the
operator answers, not that it has
anything to say yet.
Values reference charts/yalso
§6

Two values decide whether anything outside the cluster can read what the operator found, and both are off. api.enabled serves the panel and the read API without a credential, and ingress.enabled publishes them through Traefik. Turn either on deliberately, and keep ingress.allowedIPs set.

KeyDefaultDescription
Image
image.repositoryghcr.io/lukaszraczylo/yalsoImage repository.
image.tag""Image tag. Empty uses the chart's appVersion.
image.pullPolicyIfNotPresentImage pull policy.
image.pullSecrets[]Pull Secrets that already exist in the release namespace. A private image needs one. The chart creates none.
imagePullSecrets[]Same, at the top level, for a values file written against the common chart convention. Both are honoured.
tests.imagebusybox:1.37Image helm test runs. It needs an HTTP client and nothing else. Point it at a mirror on an air-gapped cluster.
Naming
nameOverride""Override the chart name.
fullnameOverride""Override the generated resource name.
Permissions
serviceAccount.createtrueCreate the ServiceAccount.
serviceAccount.name""Name to use. Empty uses the release fullname.
serviceAccount.annotations{}ServiceAccount annotations, for workload identity.
rbac.createtrueInstall the ClusterRole and its binding. Turn it off only when the permissions are managed elsewhere.
rbac.allowClusterMutationfalseAdd write verbs on foreign workloads to the ClusterRole. The operator's own client refuses them regardless, so this grants permission the code still declines to use.
leaderElectfalseEnable leader election. Also renders a Role granting the lease permission, which the ClusterRole deliberately omits.
The read API and panel
api.enabledfalseServe the read API and the panel. Nothing served is authenticated, so what can reach the port is the whole of its protection. Turn it on together with a NetworkPolicy.
api.port8082Port the API and panel listen on.
api.service.enabledtrueCreate a Service. This is what a browser or another tool connects through.
api.service.typeClusterIPService type.
api.service.port8082Service port.
api.service.annotations{}Service annotations.
Ingress (Traefik)
ingress.enabledfalsePublish the panel and API through a Traefik IngressRoute. Requires api.enabled, because there is nothing to route to otherwise.
ingress.host""Hostname to route. The only value with no sensible default.
ingress.entryPointswebsecureTraefik entry points.
ingress.tls.secretName""An existing certificate Secret in the release namespace. Empty falls back to Traefik's default certificate.
ingress.middlewares[]Middlewares, applied in order. Each needs the namespace it lives in, because a Traefik middleware is namespaced.
ingress.allowedIPs10.0.0.0/22 172.16.0.0/22Restrict the route by client address. The panel is unauthenticated, so this is the only thing between it and whoever resolves the host. An empty list removes the restriction.
ingress.annotations{}IngressRoute annotations.
Metrics and health
metrics.enabledtrueServe Prometheus metrics.
metrics.port8080Metrics port.
metrics.service.enabledtrueCreate a Service, which a scraper such as a ServiceMonitor needs.
metrics.service.typeClusterIPService type.
metrics.service.port8080Service port.
metrics.service.annotations{}Service annotations.
healthProbe.port8081Health and readiness port.
Runtime
workspace.sizeLimit1GiSize of the clone directory. The root filesystem is read only, so this is the one writable mount. Exceeding it evicts the pod, so raise it for a large monorepo.
resourcessee values.yamlRequests and limits. No CPU limit by default: analysis is bursty, and throttling it lengthens an incident rather than protecting the node.
podSecurityContextsee values.yamlRuns as user 65532, non-root, with the RuntimeDefault seccomp profile.
securityContextsee values.yamlRead-only root filesystem, no privilege escalation, all capabilities dropped.
terminationGracePeriodSeconds30Time an in-flight analysis has to stop cleanly.
Scheduling
podAnnotations{}Pod annotations.
podLabels{}Pod labels.
nodeSelector{}Node selector.
tolerations[]Tolerations.
affinity{}Affinity.
priorityClassName""Priority class.

These fail the render with the reason rather than being ignored.

- apiKey - apikey - token - password - webhookURL - webhookUrl - botToken - secretKey - accessKey - privateKey - credentials - secrets - config - llm - repositories - notifications - rules - ruleSets - replicaCount - replicas + refused at render time, with the reason
Custom resources yalso.raczylo.com
§7
KindWhat it holds
YalsoConfigThe whole configuration: model endpoints, detectors, repositories, gates, retention, notifications. One object named default.
YalsoIssueOne symptom, with its fingerprint, first and last sighting, and how many times it has been seen.
YalsoIncidentOne correlated group and every decision made about it: the analysis, the gates, the patch, the pull request and its outcome.
YalsoRuleSetInstructions you write that shape the analysis. They are quoted in the pull request, so a reader can tell which rules produced a proposal.
YalsoLessonWhat the operator concluded from what humans did with its pull requests. Applied only when you approve it.
The read API port 8082 · unauthenticated
§8

The Kubernetes API already serves the records themselves to anything holding a kubeconfig. This serves the layer above them, so every consumer is not deriving it again and drifting apart.

PathAnswers
/the panel: a timeline of every incident, its analysis, and how incidents relate
/api/v1/snapshotevery record in one consistent response
/api/v1/summarythe counts
/api/v1/issuessymptoms, filterable by namespace, detector and phase
/api/v1/incidentscorrelated groups; the filters match any member
/api/v1/groupshow incidents relate to one another
/openapi.jsonthe specification, held to the routes by a test
nothing served here is authenticated - ProposedPatch — manifest values, verbatim - AmendmentDiff — manifest values, verbatim + withheld from the projection, and a test says so

It stays off unless you give it an address. Put a NetworkPolicy in front of it.

Two limits stated first
§9

It is a retention window, not a history

Resolved issues and finished incidents are pruned on the schedule the configuration sets. Nothing can answer a question about a record that has already expired, and the API says so in its own description.

It can only patch what a manifest declares

A rendered Helm chart committed to a repository is patchable, because the object is a file. A chart rendered at deploy time is not: a patch to rendered output does not survive the next render, so the operator declines it and explains instead.

Declined, with a reason HelmSourceUnsupported
  → rendered at deploy time

SourceNotFound
  → no manifest declares it
Why the pull request is long §10
§10

Because the reasoning came from a language model, and a reviewer who cannot check it is being asked to trust it. Every proposal carries the evidence each statement cites, the gates that ran, the hashes of the evidence and the rules that produced it, and a section headed Not claimed.

Not claimed that the workload is otherwise healthy
that anything was applied
that either gate can tell whether
the fix is the right one
source
+

github.com/lukaszraczylo/yalso — MIT licensed. Written in Go on controller-runtime, distributed as a multi-architecture image and a Helm chart.