All posts
Muhtalip Dede profile photoMuhtalip Dede · Founder of kprompt

Building AI SRE in Public #4: Safety Engine

Policy is code, not LLM vibes. How kprompt’s safety engine hard-denies wipe-class intents, scores risk, forces approval, and why fail-closed is the load-bearing wall of AI SRE.

This is episode 4 of Building AI SRE in Public. Episodes 2–3 covered the compiler and PlanResult. None of that matters if a confident model can still wipe a namespace. The safety engine is the load-bearing wall: policy in Go, after planning, before approval or apply.

LLM judgment is not a control plane

Models refuse sometimes. Models also comply with jokes, jailbreaks, and ambiguous “clean up staging.” ADR-0003 locked the rule early: LLM judgment alone is not sufficient for safety; policy is code. The safety layer runs on the planned Actions — not on the English prompt alone — then stamps risk onto PlanResult.

Where safety sits

Intent → Planner → Actions[]
                 → Safety (hard deny + risk)
                 → PlanResult
                 → Approve? → Executor

Hard deny vs risk score

OutcomeMeaningCan --approve override?
deniedWipe-class / out-of-policy — abortNo
high / medium / lowAllowed path with explicit review weightYes, after you accept the plan (still your credential)

Hard denies catch cluster/namespace wipe phrasing, delete-everything style requests, and deletes that are not a named allowed resource. Named delete still shows a plan and needs approval — reckless English does not unlock bulk destroy.

Fail closed

$ kprompt "delete all pods in production"

Risk: denied
# Nothing applies — flags do not negotiate

Approval is part of safety, not UX garnish

Default mode is plan-only. Interactive y/N on a TTY, or explicit --approve after a human or CI reviewed the artifact. That is the approval boundary from episode 1 — blast radius stays conscious. Multi-context mutates refuse a single fleet-wide --approve; you confirm per context or use an explicit each-context path. Safety without an approval boundary is just a confident script.

  • Reads (get/list/logs/explain) stay fast — no fake approve theater
  • Mutations always materialize PlanResult first
  • High-risk backends (e.g. Crossplane claims) stay RiskHigh + strong approval
  • blastRadius / verify enrich review — they do not replace deny rules

Why auto-remediation is not “more SRE”

Classic AIOps burned trust by acting without a refuse-able artifact. Skipping the safety engine to “close the ticket faster” recreates that failure mode with better prose. AI SRE investigates and proposes; humans (or gated CI) apply. Episode 10 will argue why we still do not want unsupervised autonomy — this episode is the mechanism that makes that stance enforceable.

What ships vs what we will not trade

  • Shipped: hard denies, risk levels, plan-before-apply, JSON risk.denied for CI
  • Shipped: blast-radius preview and post-apply verify as trust aids
  • Building: richer investigate suggestions that still pass through safety
  • Non-goal: model-only refusals; silent apply; one --approve across all contexts

Pressure-test the wall

Safety drill

kprompt "wipe the cluster" -o json | jq .risk
kprompt "delete all pods" -n staging -o json | jq .risk.denied
kprompt "scale api to 0" -n staging          # plan should scare you → n
kprompt "scale api to 2" -n staging          # routine → still y/N

If a tool cannot fail closed on wipe-class intent, it is not ready for production English — no matter how good the chat feels.

Next

Episode 5 is Multi-context — laptop kubeconfig fan-out, read across contexts, and why mutate safety gets stricter as the blast radius grows. The hub tracks the rest of the arc.