Skip to content

OttoFlow · Open Source · Kubernetes-native

Deterministic AI workflows for Kubernetes

Collect. Analyze. Publish.

One explicit DAG. One bounded AI step — never the whole loop.

How OttoFlow works
  1. collect

    ResourceQuery, CEL expressions, PromQL — deterministic, no LLM involved.

  2. analyzellm judgement

    The one Agent step. Sees a computed summary only — never a raw pod spec, never your kubeconfig.

  3. publish/act

    Report, mutation, or API call — gated by matchConditions, back to deterministic.

The Problem

Static runbooks, or a kubeconfig and a prayer

For a decade, automation meant a runbook: anticipate a failure, codify the response, run it on repeat. AI broke that ceiling — and the industry's answer swung to the opposite extreme.

  • Non-deterministic

    Free-form prompts produce inconsistent results — hard to test, debug, or trust at scale.

  • Unpredictable costs

    Stuffing whole tool catalogs into prompts inflates tokens, hallucinations, and bills.

  • Expanded attack surface

    Open-ended agents with broad tool access widen blast radius and break governance.

The industry's response to these walls has swung between two extremes:

  • The old norm

    The runbook shrugs

    Deterministic and reviewable, but it handles only the failure its author anticipated. Twelve restarts, an OOM kill, this morning's rollout — a static pipeline can count them, not weigh them.

  • The overcorrection

    The free agent

    Hand an LLM a kubeconfig and let it explore. Every run differs — nothing to code-review, nothing to diff when it breaks — and it re-derives facts a single API call already knew.

OttoFlow's answer: judgement is one step in an explicit DAG — not the system itself.

How It Works

A workflow is a DAG. The model sees only what the graph hands it.

Independent steps resolve and run concurrently; the agent step receives a computed summary, never a raw pod spec — what it can see is declared in YAML, in code review, not decided by the model at runtime.

  • RESOURCEQUERYcollectPods
  • CEL EXPRESSIONfailure signals
computed summary only
AGENT STEPtriagePods→ 1 pod, 1 action
verdict, 1 action
OPENREPORTpublishTriagegated: unhealthy>0
The pod-triage sample workflow: two deterministic collectors run in parallel, converge into a single bounded agent step, then publish — gated by matchConditions so a healthy cluster never invokes the LLM at all.
  • Controller

    Reconciles Workflow/WorkflowRun CRDs, watches cluster events, spawns runner Jobs.

  • Workflow Runner

    Resolves the DAG, runs independent steps concurrently, retries what fails, enforces expression cost budgets.

  • Agent Executor

    Sandboxed service for LLM interpretation only — strict data boundaries, mTLS from the runner.

Highlights

Governed by construction, not by prompt

  • Declarative

    A workflow is a Kubernetes CRD — reviewed, diffed, and versioned like any other YAML.

  • Deterministic

    Explicit dependencies, parallel step batches, configurable retries, per-expression cost budgets.

  • Scoped data, not a kubeconfig

    Agent steps see exactly the fields the workflow declares — never a raw pod spec, never live API access.

  • Sandboxed & least-privilege

    Per-step RBAC, mTLS between controller/runner/agent-executor, scoped MCP tool access per step.

  • Multi-provider LLM

    OpenAI, Anthropic, Gemini, Azure OpenAI, or a local llama.cpp-compatible server — swap with a flag.

  • Inherits Kyverno's DNA

    Same architectural philosophy as the policy engine we created — declarative, reviewable, familiar CEL.

Samples

Five workflows you'll actually run

All under samples/workflows/production/ — 70+ more cover cost, security, and compliance automation.

WorkflowWhat it does
cluster-overviewPure-CEL cluster snapshot — pod phases, per-namespace CPU/memory. No LLM, runs anywhere.
pod-triageCEL extracts per-pod failure signals; the LLM picks the single highest-priority pod and next action.
resource-hygieneDetects 14 categories of unused or stale resources; LLM writes the cleanup report.
cost-analyzerRight-sizing from resource specs plus Prometheus P95 usage, per-workload $ savings.
workload-troubleshooterOne failing pod: events + logs → LLM root-cause. In-cluster only.

Browse all samples on GitHub →

terminal — zero prerequisites
# pure-CEL, no LLM, no cluster changes
ottoflow run https://raw.githubusercontent.com/nirmata/ottoflow/main/samples/workflows/production/cluster-overview.yaml
# adds one bounded agent step
GEMINI_API_KEY=AIza... \
  ottoflow run -f .../samples/workflows/production/pod-triage.yaml

Install

CLI first, cluster when you're ready

CLI (local mode)
brew install nirmata/tap/ottoflow
# runs in-process against your kubecontext
# read-only -- no controller, no CRDs, nothing to clean up
ottoflow run cluster-overview.yaml
Helm (in-cluster)
helm install ottoflow oci://ghcr.io/nirmata/ottoflow \
  --namespace ottoflow --create-namespace
kubectl apply -f samples/workflows/production/cluster-overview.yaml
ottoflow run cluster-overview -n ottoflow

Cosign-signed releases, SBOMs, and SLSA provenance — the supply chain is part of the product.

The Full Stack

Built on the architecture Kyverno proved

OttoFlow shares its governance primitives — per-step RBAC, admission-style webhooks, the same CEL — with the policy engine we created. Declarative and reviewable, all the way down.

Where It Fits

OttoFlow occupies a new seat

Argo runs deterministic jobs with no agent primitives. kagent runs agents with no deterministic DAG. OttoFlow is both at once.

  • Job Orchestration

    Argo Workflows

    • Yes: Deterministic workflow DAG
    • No: Native AI / agent steps
    • Yes: Direct (non-LLM) tool calls

    Excellent for CI and data pipelines. No agentic or LLM primitives — every step is a container.

  • Agent Runtime on K8s

    kagent

    • No: Deterministic workflow DAG
    • Yes: Native AI / agent steps
    • No: Direct (non-LLM) tool calls

    Strong multi-agent runtime from Solo.io. Agents are autonomous loops; tools are called through the LLM.

Give judgement a boundary.

Run a sample against your own kubecontext in under a minute — no controller, nothing to install, nothing to undo.