Skip to content

Rego Policy Reference

This page is the authoritative reference for all input fields available to Rego policies in Kubernaut. Use it when writing or customizing policies for Signal Processing (SP) classification and AI Analysis (AA) approval gates.

All policies are deployed as ConfigMaps and support hot-reload. See Rego Policies for an overview of each policy's purpose and default behavior.

Writing custom policies

Every input.* field listed below is available in your Rego rules. The default policies shipped with Kubernaut use only a subset -- you can reference any field to build richer logic tailored to your organization.


Signal Processing Policy

All SP classification rules live in a single policy.rego file under package signalprocessing (ADR-060). The evaluator sends a common typed input struct and queries four named rules. Each rule group is documented below.

Common Input Schema

All SP rules receive the same PolicyInput struct:

Field Type Description
input.namespace.name string Kubernetes namespace name
input.namespace.labels map[string]string All labels on the namespace
input.namespace.annotations map[string]string All annotations on the namespace
input.signal.severity string Raw severity string from the alert source
input.signal.type string Signal type (e.g., PodCrashLoop)
input.signal.source string Alert source identifier (e.g., prometheus)
input.signal.labels map[string]string Labels attached to the signal
input.workload.kind string Target resource kind (e.g., Deployment)
input.workload.name string Target resource name
input.workload.labels map[string]string All labels on the workload

Severity Rules

Rego query: data.signalprocessing.severity

Expected output: A string -- one of critical, high, medium, low, unknown

Example: PagerDuty P0--P4 Mapping

package signalprocessing

import rego.v1

severity_map := {
    "p0": "critical",
    "p1": "critical",
    "p2": "high",
    "p3": "medium",
    "p4": "low",
}

severity := severity_map[lower(input.signal.severity)] if {
    lower(input.signal.severity) in object.keys(severity_map)
}

# Fallback: unmapped severity escalates to critical (conservative)
default severity := "critical"

Example: Source-Aware Severity

package signalprocessing

import rego.v1

# PagerDuty alerts use P0-P4
severity := "critical" if {
    input.signal.source == "pagerduty"
    lower(input.signal.severity) in {"p0", "p1"}
}

# Prometheus alerts use standard severity labels
severity := input.signal.severity if {
    input.signal.source == "prometheus"
    input.signal.severity in {"critical", "high", "medium", "low"}
}

default severity := "unknown"

Environment Rules

Rego query: data.signalprocessing.environment

Expected output: {"environment": string, "source": string}

Example: Label-Based with Namespace Name Fallback

package signalprocessing

import rego.v1

# Primary: kubernaut.ai/environment namespace label
environment := {"environment": lower(env), "source": "namespace-labels"} if {
    env := input.namespace.labels["kubernaut.ai/environment"]
    env != ""
}

# Fallback: namespace name convention
environment := {"environment": "production", "source": "namespace-name"} if {
    not input.namespace.labels["kubernaut.ai/environment"]
    startswith(input.namespace.name, "prod")
}

environment := {"environment": "staging", "source": "namespace-name"} if {
    not input.namespace.labels["kubernaut.ai/environment"]
    startswith(input.namespace.name, "staging")
}

default environment := {"environment": "", "source": "unclassified"}

Example: Signal Label Override

package signalprocessing

import rego.v1

# Allow alerts to carry an explicit environment label
environment := {"environment": lower(env), "source": "signal-labels"} if {
    env := input.signal.labels["environment"]
    env != ""
}

# Otherwise use namespace label
environment := {"environment": lower(env), "source": "namespace-labels"} if {
    not input.signal.labels["environment"]
    env := input.namespace.labels["kubernaut.ai/environment"]
    env != ""
}

default environment := {"environment": "", "source": "unclassified"}

Priority Rules

Rego query: data.signalprocessing.priority

Expected output: {"priority": "P0"|"P1"|"P2"|"P3", "policy_name": string}

Priority rules can cross-reference environment and severity rules directly within the same policy file.

Example: Score-Based Priority (Default)

package signalprocessing

import rego.v1

# Severity dimension (cross-references the severity rule)
severity_score := 3 if { severity == "critical" }
severity_score := 2 if { severity == "high" }
severity_score := 1 if { severity == "medium" }
default severity_score := 0

# Environment dimension (cross-references the environment rule)
env_scores contains 3 if { environment.environment == "production" }
env_scores contains 2 if { environment.environment == "staging" }
env_scores contains 1 if { environment.environment == "development" }

# Tier label boost
env_scores contains 3 if { input.namespace.labels["tier"] == "critical" }
env_scores contains 2 if { input.namespace.labels["tier"] == "high" }

env_score := max(env_scores) if { count(env_scores) > 0 }
default env_score := 0

composite_score := severity_score + env_score

priority := {"priority": "P0", "policy_name": "score-based"} if { composite_score >= 6 }
priority := {"priority": "P1", "policy_name": "score-based"} if { composite_score == 5 }
priority := {"priority": "P2", "policy_name": "score-based"} if { composite_score == 4 }
priority := {"priority": "P3", "policy_name": "score-based"} if { composite_score < 4; composite_score > 0 }

default priority := {"priority": "P3", "policy_name": "default-catch-all"}

Example: Workload-Label Boost

package signalprocessing

import rego.v1

# Payment services always get P0
priority := {"priority": "P0", "policy_name": "payment-override"} if {
    input.workload.labels["app"] == "payment-service"
    severity in {"critical", "high"}
}

# Otherwise fall through to score-based (omitted for brevity)

Custom Labels Rules

Rego query: data.signalprocessing.labels

Expected output: map[string][]string -- key-value pairs extracted for workflow scoring

Validation limits

Custom labels are validated against hard limits: max 10 keys, max 5 values per key, max 63 chars per key, max 100 chars per value. Keys starting with kubernaut.ai/ or system/ are reserved and will be rejected.

Example: Extract kubernaut.ai/label-* Namespace Labels (Default)

package signalprocessing

import rego.v1

labels[key] := [value] if {
    some k, v in input.namespace.labels
    startswith(k, "kubernaut.ai/label-")
    key := trim_prefix(k, "kubernaut.ai/label-")
    value := v
}

Example: Combine Namespace and Workload Labels

package signalprocessing

import rego.v1

# Extract from namespace labels (kubernaut.ai/label-*)
labels[key] := [value] if {
    some k, v in input.namespace.labels
    startswith(k, "kubernaut.ai/label-")
    key := trim_prefix(k, "kubernaut.ai/label-")
    value := v
}

# Also extract from workload labels (app.kubernetes.io/*)
labels[key] := [value] if {
    some k, v in input.workload.labels
    startswith(k, "app.kubernetes.io/")
    key := trim_prefix(k, "app.kubernetes.io/")
    value := v
}

Example: Severity-Aware Custom Labels

package signalprocessing

import rego.v1

# Tag critical alerts with an escalation label (cross-references severity and environment rules)
labels["escalation"] := ["immediate"] if {
    severity == "critical"
    environment.environment == "production"
}

labels["escalation"] := ["standard"] if {
    severity != "critical"
}

AI Analysis Approval Policy

The approval policy runs after the investigation pipeline returns a successful workflow selection. It determines whether the remediation requires human approval or can proceed automatically.

Package: aianalysis.approval

Rego query: data.aianalysis.approval

Expected outputs:

Output Type Description
require_approval bool true if human approval is needed
reason string Explanation shown in the RemediationApprovalRequest

Input Fields

Signal Context

Field Type Description
input.signal_type string Signal name that triggered the analysis (e.g., OOMKilled, CrashLoopBackOff)
input.severity string Normalized severity from SP (critical, high, medium, low)
input.environment string Environment classification from SP (production, staging, etc.)
input.business_priority string Priority assigned by SP (P0--P3)

Target and Affected Resources

Field Type Description
input.target_resource.kind string Kind of the resource targeted by the signal (e.g., Deployment)
input.target_resource.name string Name of the target resource
input.target_resource.namespace string Namespace of the target resource
input.remediation_target object or null LLM-identified resource for remediation (ADR-055). null when the LLM could not identify the root cause resource
input.remediation_target.kind string Kind of the affected resource (e.g., Deployment, StatefulSet, Node)
input.remediation_target.name string Name of the affected resource
input.remediation_target.namespace string Namespace of the affected resource

Investigation Results

Field Type Description
input.confidence float LLM confidence score for the selected workflow (0.0--1.0)
input.confidence_threshold float or absent Operator-configurable threshold (default: 0.8). Set via Helm aianalysis.rego.confidenceThreshold
input.warnings []string Warnings from the investigation pipeline
input.failed_detections []string Labels that the LLM could not determine (e.g., "gitOpsManaged", "pdbProtected")

Detected Labels (Post-RCA)

These are infrastructure characteristics detected by the LLM during root cause analysis.

Field Type Description
input.detected_labels.git_ops_managed bool Whether the workload is managed by a GitOps tool
input.detected_labels.git_ops_tool string GitOps tool name (e.g., argocd, flux). Empty if not GitOps-managed
input.detected_labels.pdb_protected bool Whether a PodDisruptionBudget protects the workload
input.detected_labels.hpa_enabled bool Whether a HorizontalPodAutoscaler is configured
input.detected_labels.stateful bool Whether the workload is stateful (StatefulSet, PVC-backed)
input.detected_labels.helm_managed bool Whether the workload was deployed via Helm
input.detected_labels.network_isolated bool Whether NetworkPolicies restrict the workload
input.detected_labels.service_mesh string Service mesh name (e.g., istio, linkerd). Empty if none

Custom Labels and Business Classification

Field Type Description
input.custom_labels map[string][]string Operator-defined labels from the custom labels policy
input.business_classification.business_unit string Business unit from the business policy
input.business_classification.service_owner string Service owner team
input.business_classification.criticality string Criticality level (critical, high, medium, low)
input.business_classification.sla_requirement string SLA tier (e.g., platinum, gold, silver, bronze)

Example: Default Policy (Environment-Gated)

The default policy shipped with Kubernaut requires approval for all production remediations and when the LLM cannot identify the remediation target:

Reason string backward compatibility

The risk_factors reason strings (e.g., "Missing affected resource") are operator-facing and intentionally kept unchanged from earlier releases, even though the rule and input field were renamed to has_remediation_target / input.remediation_target. This avoids breaking dashboards or alerting rules that match on these strings.

package aianalysis.approval

import rego.v1

default require_approval := false
default reason := "Auto-approved"

has_remediation_target if {
    input.remediation_target
    input.remediation_target.kind != ""
}

is_production if {
    input.environment == "production"
}

require_approval if { not has_remediation_target }
require_approval if { is_production }

risk_factors contains {"score": 90, "reason": "Missing affected resource"} if {
    not has_remediation_target
}
risk_factors contains {"score": 70, "reason": "Production environment"} if {
    is_production
}

all_scores contains f.score if { some f in risk_factors }
max_risk_score := max(all_scores) if { count(all_scores) > 0 }
reason := f.reason if { some f in risk_factors; f.score == max_risk_score }

Example: Confidence-Gated Approval

Require approval only when confidence is below the threshold:

package aianalysis.approval

import rego.v1

default require_approval := false
default reason := "Auto-approved (high confidence)"

default confidence_threshold := 0.8

confidence_threshold := input.confidence_threshold if {
    input.confidence_threshold
}

is_high_confidence if {
    input.confidence >= confidence_threshold
}

has_remediation_target if {
    input.remediation_target
    input.remediation_target.kind != ""
}

# Always require approval when affected resource is unknown
require_approval if { not has_remediation_target }

# Low confidence in any environment
require_approval if { not is_high_confidence }

risk_factors contains {"score": 90, "reason": "Missing affected resource"} if {
    not has_remediation_target
}
risk_factors contains {"score": 65, "reason": "Low confidence score"} if {
    not is_high_confidence
}

all_scores contains f.score if { some f in risk_factors }
max_risk_score := max(all_scores) if { count(all_scores) > 0 }
reason := f.reason if { some f in risk_factors; f.score == max_risk_score }

Example: Business-Criticality-Aware Approval

Gate approval based on business classification and detected infrastructure characteristics:

package aianalysis.approval

import rego.v1

default require_approval := false
default reason := "Auto-approved"

has_remediation_target if {
    input.remediation_target
    input.remediation_target.kind != ""
}

# Critical business services always require approval
require_approval if {
    input.business_classification.criticality == "critical"
}

# Stateful workloads in production require approval
require_approval if {
    input.environment == "production"
    input.detected_labels.stateful == true
}

# GitOps-managed workloads with low confidence require approval
require_approval if {
    input.detected_labels.git_ops_managed == true
    input.confidence < 0.9
}

# PDB-protected workloads require approval (disruption risk)
require_approval if {
    input.detected_labels.pdb_protected == true
}

require_approval if { not has_remediation_target }

risk_factors contains {"score": 85, "reason": "Critical business service"} if {
    input.business_classification.criticality == "critical"
}
risk_factors contains {"score": 90, "reason": "Missing affected resource"} if {
    not has_remediation_target
}
risk_factors contains {"score": 75, "reason": "Stateful production workload"} if {
    input.environment == "production"
    input.detected_labels.stateful == true
}
risk_factors contains {"score": 70, "reason": "GitOps-managed with low confidence"} if {
    input.detected_labels.git_ops_managed == true
    input.confidence < 0.9
}
risk_factors contains {"score": 60, "reason": "PDB-protected workload"} if {
    input.detected_labels.pdb_protected == true
}

all_scores contains f.score if { some f in risk_factors }
max_risk_score := max(all_scores) if { count(all_scores) > 0 }
reason := f.reason if { some f in risk_factors; f.score == max_risk_score }

Example: Custom-Label-Driven Approval

Use operator-defined custom labels to control approval flow:

package aianalysis.approval

import rego.v1

default require_approval := false
default reason := "Auto-approved"

# Require approval if custom label "approval-required" is set to "true"
require_approval if {
    "true" in input.custom_labels["approval-required"]
}

# Require approval for specific teams
require_approval if {
    "payments" in input.custom_labels["team"]
    input.environment == "production"
}

risk_factors contains {"score": 80, "reason": "Team-mandated approval for payments in production"} if {
    "payments" in input.custom_labels["team"]
    input.environment == "production"
}
risk_factors contains {"score": 70, "reason": "Namespace-level approval requirement"} if {
    "true" in input.custom_labels["approval-required"]
}

all_scores contains f.score if { some f in risk_factors }
max_risk_score := max(all_scores) if { count(all_scores) > 0 }
reason := f.reason if { some f in risk_factors; f.score == max_risk_score }

Quick Reference: All Input Fields

Signal Processing

Policy Input Path Fields
Severity input.signal.* severity, type, source
Environment input.namespace.*, input.signal.* namespace.name, namespace.labels, signal.labels
Priority input.signal.*, input.namespace.*, input.workload.* signal.severity, signal.source, namespace.labels, workload.labels
Business input.namespace.*, input.workload.*, input.* namespace.name/labels/annotations, workload.kind/name/labels/annotations, environment
Custom Labels input.namespace.*, input.workload.*, input.signal.* namespace.name/labels/annotations, workload.kind/name/labels/annotations, workload.ownerChain, signal.type/severity/source

AI Analysis Approval

Category Input Path Fields
Signal context input.* signal_type, severity, environment, business_priority
Target resource input.target_resource.* kind, name, namespace
Remediation target input.remediation_target.* kind, name, namespace (or null)
Investigation input.* confidence, confidence_threshold, warnings, failed_detections
Detected labels input.detected_labels.* git_ops_managed, git_ops_tool, pdb_protected, hpa_enabled, stateful, helm_managed, network_isolated, service_mesh
Classification input.custom_labels, input.business_classification.* custom_labels (map), business_unit, service_owner, criticality, sla_requirement

Source Code References

Component Source File
SP unified evaluator pkg/signalprocessing/evaluator/evaluator.go
SP policy input types pkg/signalprocessing/evaluator/types.go
SP signal mode classifier pkg/signalprocessing/classifier/signalmode.go
SP example policy charts/kubernaut/examples/signalprocessing-policy.rego
SP deploy policies deploy/signalprocessing/policies/
AA approval evaluator pkg/aianalysis/rego/evaluator.go
AA input builder pkg/aianalysis/handlers/analyzing.go
AA approval policy (test) test/unit/aianalysis/testdata/policies/approval.rego