Policy Reports
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 towarn, everything else topass. 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 — sokubectl get eventsandkubectl describesurface 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:
| API | Kinds | Typically installed by |
|---|---|---|
openreports.io/v1alpha1 | Report, ClusterReport | The OpenReports CRDs; Kyverno with OpenReports output enabled; the Nirmata runtime sensor |
wgpolicyk8s.io/v1alpha2 | PolicyReport, ClusterPolicyReport | Stock 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.
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.Enable the sink in your values file:
audit:policyReport:enabled: trueUpgrade the release:
helm upgrade aicontrols nirmata/aicontrols -n aicontrols -f values.yamlThis renders a ClusterRole and ClusterRoleBinding granting the AIControls ServiceAccount
get,list,create,update, andpatchon both report API groups, pluscreateandpatchon 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:
| State | Meaning |
|---|---|
available | A report API was resolved; apiVersion and kind name it. Reports are being written. |
unavailable | Discovery answered, and this cluster serves neither report API. reason says so. Install the CRDs. |
unknown | Discovery 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
ClusterReportorClusterPolicyReport. - 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.