Configuration

Configure ToolConfig, LLMConfig, and the Remediator custom resource.

The Remediator Agent is configured through three Kubernetes custom resources:

ResourcePurpose
ToolConfigGit provider credentials and PR defaults
LLMConfigAI provider settings (Nirmata AI, AWS Bedrock, Azure OpenAI)
RemediatorWhat to scan, when to run, and what actions to take

Nirmata Platform Authentication

The Helm chart’s nirmata.auth value selects how — or whether — the agent authenticates to Nirmata Control Hub.

nirmata.authCredential injectedUse when
serviceAccountToken (default)SERVICE_ACCOUNT_TOKEN from nirmata.serviceAccountTokenSecretStandard install
apiTokenAPI_TOKEN from nirmata.apiTokenSecretYou use an API token instead of a service account
noneNoneYou supply your own LLM provider and your own Git credentials

Set nirmata.auth=none to run the agent with no Nirmata platform credential at all — for example an install with no Control Hub connectivity, using AWS Bedrock plus a GitHub PAT. The agent still reaches your LLM provider and Git provider, so this removes the dependency on Control Hub, not on outbound network access. The following Remediator features require a credential and are unavailable with none:

  • The nirmataAI LLM provider
  • The nirmata-app Git authentication method
  • Policy exception requests (the @nirmatabot request-exception command)

The chart validates this at install time rather than letting it fail at runtime. Combining nirmata.auth=none with llm.provider=nirmataAI or tool.credentials.method=nirmata-app aborts the install with an explanatory error.


ToolConfig

ToolConfig defines how the agent authenticates with your Git provider and sets defaults for the pull requests it creates.

GitHub — Personal Access Token

kubectl create secret generic github-pat-token \
  --from-literal=token=GITHUB_PAT_TOKEN \
  --namespace nirmata

kubectl apply -f - <<EOF
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: ToolConfig
metadata:
  name: toolconfig-sample
  namespace: nirmata
spec:
  type: github
  credentials:
    method: pat
    pat:
      tokenSecretRef:
        name: github-pat-token
        namespace: nirmata
        key: token
  defaults:
    git:
      pullRequests:
        branchPrefix: "remediation-"
        titleTemplate: "[Auto-Remediation] Fix policy violations: "
        commitMessageTemplate: "Auto-fix: Remediate policy violations: "
        customLabels:
          - "auto-remediation"
          - "security"
        systemLabels:
          - "clusterName"
          - "namespace"
EOF
```text

### GitHub — Nirmata GitHub App (Recommended)

Using the Nirmata GitHub App avoids managing secrets manually and provides automatic token rotation. Follow the [GitHub Authentication guide](../github-authentication/) to set up the App integration first.

```yaml
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: ToolConfig
metadata:
  name: toolconfig-sample
  namespace: nirmata
spec:
  type: github
  credentials:
    method: nirmata-app
  defaults:
    git:
      pullRequests:
        branchPrefix: "remediation-"
        titleTemplate: "[Auto-Remediation] Fix policy violations: "
        commitMessageTemplate: "Auto-fix: Remediate policy violations: "
        customLabels:
          - "auto-remediation"
        systemLabels:
          - "clusterName"
          - "namespace"
```text

### GitHub — Custom GitHub App

Use your own GitHub App when it must be owned by your organization, or when the agent runs with `nirmata.auth=none`. See the [GitHub Authentication guide](../github-authentication/#3-custom-github-app) for creating the App and the permissions it needs.

```bash
kubectl create secret generic github-app-private-key \
  --from-file=private-key.pem=/path/to/your-app.private-key.pem \
  --namespace nirmata
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: ToolConfig
metadata:
  name: github-app-tool
  namespace: nirmata
spec:
  type: github
  credentials:
    method: app
    app:
      appId: 123456                 # integer, not a string
      # installationId: 78901234    # optional — auto-discovered when omitted
      privateKeySecretRef:
        name: github-app-private-key
        key: private-key.pem
  defaults:
    git:
      pullRequests:
        branchPrefix: "remediation-"

The private key Secret must be in the same namespace as the ToolConfig.

GitLab

kubectl create secret generic gitlab-pat-token \
  --from-literal=token=GITLAB_PAT_TOKEN \
  --namespace=nirmata

kubectl apply -f - <<EOF
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: ToolConfig
metadata:
  name: toolconfig-sample
  namespace: nirmata
spec:
  type: gitlab
  credentials:
    method: pat
    pat:
      tokenSecretRef:
        name: gitlab-pat-token
        namespace: nirmata
        key: token
EOF
```text

### Private Certificate Authorities

The agent reaches your Git provider at `github.com` or `gitlab.com` — there is no ToolConfig setting for a GitHub Enterprise Server or self-managed GitLab API endpoint. Use `spec.tls` when that traffic passes through a TLS-intercepting proxy or egress gateway presenting a corporate certificate. Without the CA bundle, the agent fails with a certificate verification error.

```bash
kubectl create secret generic git-ca-bundle \
  --from-file=ca.crt=/path/to/corporate-ca.pem \
  --namespace nirmata
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: ToolConfig
metadata:
  name: toolconfig-sample
  namespace: nirmata
spec:
  type: gitlab
  credentials:
    method: pat
    pat:
      tokenSecretRef:
        name: gitlab-pat-token
        namespace: nirmata
        key: token
  tls:
    caBundleSecretRef:
      name: git-ca-bundle
      namespace: nirmata
      key: ca.crt

spec.tls.insecureSkipVerify: true disables certificate verification entirely. Use it only to confirm that a failure is certificate-related — never in production.

Pull Request Labels

ToolConfig supports two label types:

  • customLabels — static labels always applied to every PR (e.g., auto-remediation, security)
  • systemLabels — dynamic labels computed at runtime from remediation context:
System LabelValue
branchThe Git branch being remediated
clusterNameThe cluster where violations were found
appNameArgoCD application name (Hub Mode only)
namespaceKubernetes namespace containing violations

LLMConfig

LLMConfig specifies the AI provider used to generate remediation plans.

Nirmata AI (Default)

The Helm chart creates this automatically. Authentication uses the Service Account token injected at deployment time — no additional secrets needed.

apiVersion: serviceagents.nirmata.io/v1alpha1
kind: LLMConfig
metadata:
  name: nirmata-agent-llm
  namespace: nirmata
spec:
  type: nirmataAI
  nirmataAI:
    model: ""   # empty string uses the current default model
```text

### AWS Bedrock

For EKS clusters with Pod Identity Agent:

```yaml
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: LLMConfig
metadata:
  name: nirmata-agent-llm
  namespace: nirmata
spec:
  type: bedrock
  bedrock:
    model: MODEL_ARN_OR_INFERENCE_ARN
    region: AWS_REGION
```text

Bedrock also accepts `credentialsSecretRef` (a Secret holding static AWS credentials), `roleArn`, and `externalId` if Pod Identity is not available.

<details>
<summary>Full AWS IAM setup</summary>

```bash
aws iam create-role \
  --role-name remediator-agent-role \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": { "Service": "pods.eks.amazonaws.com" },
      "Action": ["sts:AssumeRole", "sts:TagSession"]
    }]
  }'

aws iam put-role-policy \
  --role-name remediator-agent-role \
  --policy-name BedrockInvokePolicy \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
      "Resource": "arn:aws:bedrock:<REGION>:<ACCOUNT_ID>:application-inference-profile/<PROFILE>"
    }]
  }'

aws eks create-pod-identity-association \
  --cluster-name <CLUSTER_NAME> \
  --namespace nirmata \
  --service-account nirmata-agent \
  --role-arn arn:aws:iam::<ACCOUNT_ID>:role/remediator-agent-role
```text
</details>

### Azure OpenAI

```bash
kubectl create secret generic azure-openai-credentials \
  --from-literal=api-key=AZURE_API_KEY \
  -n nirmata
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: LLMConfig
metadata:
  name: nirmata-agent-llm
  namespace: nirmata
spec:
  type: azure-openai
  azureOpenAI:
    endpoint: https://YOUR_RESOURCE_NAME.openai.azure.com/
    deploymentName: DEPLOYMENT_NAME
    apiKeySecretRef:
      name: azure-openai-credentials
      key: api-key
      namespace: nirmata
```yaml

### Anthropic API

Calls the Anthropic API directly with your own API key.

```bash
kubectl create secret generic anthropic-credentials \
  --from-literal=api-key=ANTHROPIC_API_KEY \
  -n nirmata
apiVersion: serviceagents.nirmata.io/v1alpha1
kind: LLMConfig
metadata:
  name: nirmata-agent-llm
  namespace: nirmata
spec:
  type: anthropic
  anthropic:
    model: claude-sonnet-5          # required — no default
    apiKeySecretRef:
      name: anthropic-credentials
      namespace: nirmata
      key: api-key
    # baseURL: https://ai-gateway.internal

model is required and has no default, so the model in use never changes silently on an agent upgrade. Set baseURL — including the scheme — to route requests through an internal AI gateway or proxy instead of https://api.anthropic.com.


Remediator

The Remediator resource ties everything together: it defines the environment mode, which clusters or applications to target, the schedule, and what actions to take.

Environment Modes

ModeUse Case
localClusterScan the cluster where the agent is installed
argoHubUse ArgoCD to manage violations across multiple clusters
fluxHubUse FluxCD to manage violations across multiple clusters

Direct repository scanning (target.vcs) is not an environment type — it is configured alongside any of the three modes above. See VCS Target Mode.

ArgoCD Hub Mode (Multi-Cluster)

apiVersion: serviceagents.nirmata.io/v1alpha1
kind: Remediator
metadata:
  name: remediator-argo-hub
  namespace: nirmata
spec:
  environment:
    type: argoHub
  target:
    argoHub:
      appSelector:
        allApps: true    # or specify names / labelSelector
  remediation:
    triggers:
      - schedule:
          crontab: "0 */6 * * *"
    llmConfigRef:
      name: nirmata-agent-llm
      namespace: nirmata
    gitCredentials:
      name: toolconfig-sample
      namespace: nirmata
    eventPolling:
      enabled: true
      intervalMinutes: 5
    actions:
      - type: CreatePR
        toolRef:
          name: toolconfig-sample
          namespace: nirmata

Targeting specific clusters or apps:

target:
  argoHub:
    clusterNames:
      - production-cluster
      - staging-cluster
    appSelector:
      names:
        - nginx-demo
        - web-app
      labelSelector:
        matchLabels:
          team: platform
          environment: production

FluxCD Hub Mode (Multi-Cluster)

Targets workloads managed by Flux. The agent selects Flux Kustomization and/or HelmRelease objects, then follows each one’s sourceRef to its GitRepository to discover the repository URL and branch to open the PR against — so no repo-to-namespace mapping is needed.

apiVersion: serviceagents.nirmata.io/v1alpha1
kind: Remediator
metadata:
  name: remediator-flux-hub
  namespace: nirmata
spec:
  environment:
    type: fluxHub
  target:
    fluxHub:
      fluxNamespace: flux-system   # where Flux controllers run; reference only
      kustomizationSelector:
        names:
          - my-app
          - backend-api
  remediation:
    triggers:
      - schedule:
          crontab: "0 */6 * * *"
    llmConfigRef:
      name: nirmata-agent-llm
      namespace: nirmata
    gitCredentials:
      name: toolconfig-sample
      namespace: nirmata
    actions:
      - type: CreatePR
        toolRef:
          name: toolconfig-sample
          namespace: nirmata

Set at least one of kustomizationSelector or helmReleaseSelector — the Helm chart refuses to install without one, and a Remediator applied directly with neither is admitted but selects no targets. Set both to remediate both object types. Each selector accepts:

FieldBehavior
namesSelect specific objects by name
namespacesRestrict which namespaces are searched. In hub-driven setups each spoke usually has a dedicated namespace on the hub. Empty means all namespaces
labelSelectorSelect by labels, e.g. nirmata.io/remediation: "enabled"
allKustomizations / allHelmReleasesSelect everything found in the searched namespaces; other fields are ignored
target:
  fluxHub:
    kustomizationSelector:
      namespaces:
        - production
        - staging
      labelSelector:
        matchLabels:
          nirmata.io/remediation: "enabled"
    helmReleaseSelector:
      allHelmReleases: true

Flux API versions are detected automatically, covering Flux 0.x through 2.7+ (v1/v1beta2/v1beta1 for Kustomization and GitRepository, v2/v2beta2/v2beta1 for HelmRelease).

Local Cluster Mode

See the example in Getting Started.

VCS Target Mode (Direct Repository Scanning)

Scan Git repositories directly without requiring a running cluster:

spec:
  environment:
    type: localCluster
  target:
    vcs:
      policies:
        - name: pod-security-policy
          repo: https://github.com/your-org/policies
          path: kyverno/pod-security.yaml
          ref: main
      resources:
        - name: web-app-deployment
          repo: https://github.com/your-org/manifests
          path: deployments/web-app.yaml
          ref: main
          policyRefs:
            - pod-security-policy
  remediation:
    triggers:
      - schedule:
          crontab: "0 */6 * * *"
    llmConfigRef:
      name: nirmata-agent-llm
      namespace: nirmata
    actions:
      - type: CreatePR
        toolRef:
          name: toolconfig-sample
          namespace: nirmata

Remediation Modes

spec.remediation.remediationMode selects how fixes are generated.

ModeBehavior
ai (default)An LLM generates the remediation. llmConfigRef is required
prescriptiveFixes are computed deterministically with no LLM call anywhere in the path. llmConfigRef is not required and is ignored if set

Prescriptive mode is currently scoped to resource-optimization violations: it right-sizes container CPU and memory from VerticalPodAutoscaler recommendations and from bounds defined in the policy itself. Existing Remediators are unaffected — the field defaults to ai.

Scoping reuses the standard Kyverno category annotation, so nothing new has to be labelled on your workloads:

spec:
  remediation:
    remediationMode: prescriptive
    resourceOptimization:
      policyCategories:
        - "Resource Management"   # default
    triggers:
      - schedule:
          crontab: "0 */6 * * *"
    gitCredentials:
      name: toolconfig-sample
      namespace: nirmata
    actions:
      - type: CreatePR
        toolRef:
          name: toolconfig-sample
          namespace: nirmata

A violation is remediated only when its policy’s policies.kyverno.io/category annotation appears in policyCategories. Which workloads are in scope is decided by the policy’s own match block — there is nothing to configure here for that.

Prerequisites

  1. A Kyverno policy annotated with a matching category:

    metadata:
      annotations:
        policies.kyverno.io/category: Resource Management
    
  2. For usage-based right-sizing, a VerticalPodAutoscaler in recommendation-only mode for each workload:

    apiVersion: autoscaling.k8s.io/v1
    kind: VerticalPodAutoscaler
    metadata:
      name: nginx
      namespace: my-app-ns
    spec:
      targetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: nginx
      updatePolicy:
        updateMode: "Off"   # recommendation only — never restarts pods
    

    Boundary policies (for example “the CPU limit must not exceed 1”) are satisfied from the policy bound alone and need no VPA.

Current limits

  • Workloads with neither a VPA recommendation nor a computable policy bound are skipped and recorded — never guessed.
  • Helm charts and kustomize-overlay value indirection are skipped in this phase.
  • The Helm chart always renders llmConfigRef and has no remediationMode value, so apply a prescriptive Remediator as a manifest rather than through the chart.

Actions

ActionBehavior
CreatePROpens a pull/merge request with the fix
DryRunLogs the planned file changes and creates nothing. Useful for a first evaluation. Raise logging.verbosity to 2 to log the proposed file contents

toolRef is required by the schema for both action types.

CI Checks

Every remediation PR the agent opens on GitHub also gets two check runs, so a reviewer can see whether the proposed fix is valid before merging. Nothing needs to be configured — they are created automatically alongside the PR.

CheckValidates
Kubernetes Resource ValidationThe remediated manifest against the Kubernetes OpenAPI schema — that the change is structurally valid
Policy ComplianceThe remediated manifest against the Kyverno policies, locally — that the change actually resolves the violation

Each check reports one of three conclusions:

ConclusionMeaning
successValidation passed
failureThe proposed change is invalid, or still violates a policy. Review before merging
neutralValidation was skipped — for example the policy could not be evaluated locally

Both checks are advisory: they annotate the PR but never block it, and a failure to create them is logged without affecting the PR. Policy Compliance runs the Kyverno validator directly, so it involves no LLM call and works the same in prescriptive mode.

Check runs are a GitHub API feature. GitLab merge requests get no equivalent, and checks are also skipped when no commit SHA is available.

Confidence-Based Actions

Control when automated PRs are created based on the AI’s confidence in its fix:

remediation:
  actions:
    - type: CreatePR
      confidence:
        - high     # only create PRs when AI is highly confident
      toolRef:
        name: toolconfig-sample
        namespace: nirmata
ConfidenceMeaning
highThe configured AI provider is highly confident the fix is correct and safe
lowA potential fix was found but human review is recommended

Confidence is set by the AI provider, so gating applies to ai mode. Prescriptive plans are always high.

Filtering by Severity

remediation:
  filters:
    policySelector:
      matchSeverity:
        - high
        - critical
```yaml

---

## Split PR

The Split PR feature lets you split a pull request containing fixes for multiple policies into separate PRs — useful when different policies need different reviewers or approval workflows.

### How to Split

Add a comment to the PR mentioning `@nirmatabot`:

```text
@nirmatabot split-pr require-run-as-non-root disallow-privileged-containers
  • One or more policy names (space-separated) after split-pr
  • Use the exact policy names as they appear in the PR

What Happens

  • Original PR — updated to contain only the remaining policies; a comment is added with a link to the new PR
  • New PR — created on a new branch (prefixed splitpr-) with only the specified policies; links back to the original PR
  • Both PRs are tracked independently by the agent

Request a Policy Exception

When a violation should not be fixed, request an exception from the PR instead. This raises a Policy Exception Request in Nirmata Control Hub for approval.

@nirmatabot request-exception <policy-name> [<policy-name> ...] [duration] [reason]
ArgumentRequiredDescription
policy-nameYesOne or more Kyverno policy names to except. Use the names as they appear in the PR
durationNoA number of days (7d, 30d) or permanent. Defaults to permanent when omitted
reasonNoFree-text justification — all text after the duration token. Shown in the Control Hub approval UI
@nirmatabot request-exception disallow-capabilities-strict
@nirmatabot request-exception disallow-capabilities-strict 30d
@nirmatabot request-exception disallow-host-ports restrict-apparmor-profiles 7d Approved by security team

A reason is only parsed after a duration token, so include a duration whenever you want to record a justification.

What Happens

  1. The named policies are removed from the remediation PR, and the agent comments with the request name and the policies still covered by the PR.
  2. A Policy Exception Request is created in Control Hub and approvers are notified by email. Track it under Policies → Exception Requests.
  3. On approval, Control Hub opens a second PR containing a Kyverno PolicyException manifest scoped to that exact resource. Merge that PR to activate the exception.

If a request already exists for a policy, the agent reports it and skips that policy rather than filing a duplicate.