Skip to main content

Policy Reports

Self-Hosted Kubernetes

AIControls can mirror every governance decision into Kubernetes policy-report custom resources and emit a Warning Event on each deny. If your cluster already runs Policy Reporter, or any dashboard that reads policy reports, this puts AI agent findings in the same place as your admission-policy findings — no separate integration.

What gets written

When the sink is enabled, AIControls writes:

  • One report per agent per rolling hour, in the namespace the agent runs in. Each governance decision becomes one result: a deny maps to fail, an audit or approval-required decision maps to warn, everything else to pass. The result carries the policy name, the rule name, the message, and the tool name as the subject resource.
  • A Warning Event on each deny, with reason ToolCallDenied, attached to the agent in that namespace — so kubectl get events and kubectl describe surface the denial next to the workload.

The sink is additive. Nothing about the audit log, the dashboard, or enforcement changes; the reports are a second, cluster-native view of the same decisions.

Which report API is used

There are two policy-report APIs in the ecosystem, and which one a cluster serves is a property of that cluster, not something you configure:

APIKindsTypically installed by
openreports.io/v1alpha1Report, ClusterReportThe OpenReports CRDs; Kyverno with OpenReports output enabled; the Nirmata runtime sensor
wgpolicyk8s.io/v1alpha2PolicyReport, ClusterPolicyReportStock Kyverno installs

AIControls detects this at startup rather than assuming it. It prefers openreports.io/v1alpha1 — the successor API — and falls back to wgpolicyk8s.io/v1alpha2 when only that is served.

Exactly one API is ever written. On a cluster serving both, you get OpenReports only. Writing both would duplicate every finding for consumers that watch both groups (Policy Reporter 3.2.0 and later do), and a duplicated report is worse than a single one.

If neither API is served, the sink reports itself unavailable with a reason instead of silently writing nothing — see Check what the sink resolved. Installing the CRDs later is picked up automatically within a few minutes; you do not need to restart AIControls.

Enable the sink

The sink is off by default, because it needs cluster-wide permission to create and update reports — a cluster that has not asked for the feature is not asked for the grant.

  1. Install a report API if your cluster does not already serve one. If you run Kyverno, you already have wgpolicyk8s.io. Otherwise install the OpenReports CRDs from the OpenReports reports-api releases.

  2. Enable the sink in your values file:

    audit:
    policyReport:
    enabled: true
  3. Upgrade the release:

    helm upgrade aicontrols nirmata/aicontrols -n aicontrols -f values.yaml

    This renders a ClusterRole and ClusterRoleBinding granting the AIControls ServiceAccount get, list, create, update, and patch on both report API groups, plus create and patch on Events. Both groups are granted because the choice is made at runtime; only the one the cluster serves is ever written.

Check what the sink resolved

A reporting integration that writes nothing looks exactly like a cluster with nothing to report, so AIControls surfaces the sink's resolved state rather than only logging it at startup.

The Health view lists a PolicyReport sink component whenever the sink is enabled. It reads either as the API being written — for example writing Report to openreports.io/v1alpha1 — or as an unhealthy row carrying the reason it could not resolve one.

The same facts are available from the admin API for scripting:

curl -H "Authorization: Bearer $TOKEN" \
https://aicontrols.example.com/api/v1/audit/report-sink/status
{
"configured": true,
"state": "available",
"apiVersion": "openreports.io/v1alpha1",
"kind": "Report",
"probedAtUnix": 1786000000
}

state is one of:

StateMeaning
availableA report API was resolved; apiVersion and kind name it. Reports are being written.
unavailableDiscovery answered, and this cluster serves neither report API. reason says so. Install the CRDs.
unknownDiscovery itself did not answer, so nothing was established either way. reason carries the underlying error.

configured: false means the sink is not enabled on this deployment at all — check audit.policyReport.enabled.

Verify reports are landing

# OpenReports clusters
kubectl get reports.openreports.io -A -l app.kubernetes.io/managed-by=aicontrol

# Policy WG clusters
kubectl get policyreports.wgpolicyk8s.io -A -l app.kubernetes.io/managed-by=aicontrol

Reports are not written per decision. The sink batches by rolling hour and writes when one of three things happens: 100 decisions accumulate for an agent, the hour bucket ages out (so up to roughly an hour after the first call in that hour), or the proxy shuts down. On a quiet cluster, expect the first report within the hour rather than within seconds — and nothing at all until an agent actually makes a governed call.

Limitations

  • Reports are namespaced only; AIControls does not write ClusterReport or ClusterPolicyReport.
  • The sink is enabled cluster-wide or not at all; there is no per-namespace or per-agent filter.
  • Results accumulate into the current rolling-hour report and are not deduplicated — a chatty agent produces a long results list. Retention is governed by whatever prunes reports in your cluster, not by AIControls.