Skip to main content

Access policy

Reference

A workload's access policy is its Workload Blueprint's Access Binding plus the workload's own override. The bindings hold the grants, and the override carries references and deny lists that only narrow or add access for one workload.

Self-managed installations only

Workload Trust is available only in private, self-managed AIControls installations, on premises or in a cloud cluster that you run. It needs a dedicated cluster that you run, with Kyverno, Kyverno Runtime, and cluster OIDC and RBAC access in place. Nirmata-hosted (SaaS) workspaces don't include it. See Feature availability.

Document​

A workload's override document has this shape:

{
"bindingRefs": ["ab_github_ro"],
"deny": {
"toolSelectors": [{ "servers": ["*"], "tools": ["delete_repo"] }],
"modelSelectors": [{ "models": ["gpt-*"], "providers": ["azure"], "upstreams": ["azure-prod"] }]
}
}
FieldMeaning
bindingRefsAccess Binding IDs, in addition to the Blueprint's one Access Binding. The referencing workload becomes a derived subject of each binding.
deny.toolSelectorsMCP tools to deny. servers and tools accept * and simple globs.
deny.modelSelectorsLLM models to deny. models accepts * and simple globs. An empty providers list matches every provider, including an unresolved one. An optional upstreams list narrows the deny to calls that resolve to a named LLM upstream, and also matches a call whose upstream isn't resolved yet. See Upstream references.
auditUnmatchedOverrideSee Workload access.
intentA per-workload override of the Runtime Binding's access levels: any subset of the registration declaration fields type, autonomy, network, files, exec, and kubernetes, without name. Each section you set replaces the whole section from the Blueprint or its Runtime Binding. Every present section must name its access tier. The schema refuses intent.llm and intent.mcp: grant models and MCP tools with Access Bindings.
dataClassificationThe workload's data classification ceiling.

A Workload Blueprint isn't a policy document. It's a Purpose, a profile, one Access Binding, one Runtime Binding, and a starting mode, and it has no deny list and no bindingRefs. See Edit a Workload Blueprint. To block a grant for one workload, use Block for this workload, which writes the override's deny. To change the grants for every workload on a Blueprint, edit its Access Binding behind an impact preview; see Review and restore a binding change.

An override's bindingRefs can't name a binding that another workload owns; AIControls refuses the write with 409 owned_binding_reference.

The schema is closed. Field names are case-sensitive and must match the names above exactly; AIControls rejects any other field, including a differently cased one such as Deny, two fields whose names differ only in case, a null value, and the old string-list form of deny. A rejected document returns 400.

The effective access policy ID, published as object.agent.workload.accessPolicy, identifies the workload's Blueprint and the references and blocks in its override. Workloads of one Blueprint with no override share one ID. The ID changes when the workload's Blueprint or its override references or blocks change. Binding contents are live and don't change the ID.

Compiled policies​

Policy namePlaneSourceWhat it does
workloadaccess:<id>:deny:mcpMCPThe workload's deny.toolSelectorsDenies a matching tool call for the workload.
workloadaccess:<id>:deny:llmLLMThe workload's deny.modelSelectorsDenies a matching LLM call for the workload.
accessbinding:<binding>:llm-allow-markerLLMAn allow-mode binding's modelSelectorsGrants matching models to the binding's subjects and sets object.evaluation.accessMatched. It never blocks.
accessbinding:<binding>:llmLLMAnother mode's modelSelectorsApplies the binding's decision mode to matching models.
workloadaccess:generation-staleMCP, LLM, HTTPThe evaluatorDenies a workload whose access snapshot is newer than the policies this replica compiled. See Generation fence.

Workload deny entries only narrow access. A deny beats any referenced grant, because the most restrictive access decision wins. A model deny with providers also matches a call whose upstream provider is unresolved, and a model deny with upstreams also matches a call whose upstream is unresolved, so an unresolved call can't dodge a deny.

When you block an upstream-scoped grant with Block for this workload, the deny entry keeps the grant's upstreams, so it blocks the model only on those upstreams. Removing a name from a deny's upstreams list widens access, because the model is no longer blocked on that upstream; the impact preview asks you to acknowledge it.

Workload deny policies are request-phase access policies. Policy exceptions can apply to them like other binding policies. Containment is not a compiled policy: AIControls denies a contained workload before any policy runs, and no exception can lift that deny.

Model selectors on Access Bindings​

Each selector has models (required), an optional route, and two optional narrowing lists, providers and upstreams. A selector that names a route can't also list providers or upstreams.

On an allow-mode binding, route names the model route the grant's subjects use for the selector's models. The route must be one the router serves, or the save returns 400. Two bindings that grant the same subject the same model pattern at the same subject level through different routes are refused with 409 route_grant_conflict. See Access Binding route grants.

Decision modeSelector with no providersSelector with providers
AllowMatches the model.Matches the model only when the resolved upstream provider is listed.
Audit, Warn, Hold, DenyMatches the model.Matches the model when the upstream provider is listed or unresolved.

Upstream references​

upstreams lists names of configured LLM upstreams. A call matches only when its resolved upstream name, the CEL fact object.llm.upstream.name, is in the list. Workload deny entries (deny.modelSelectors) use the same references with the same rules.

  • Names are exact. Globs aren't supported.
  • AIControls validates the names against the configured LLM upstreams when you save. An unknown name returns 400 unknown_llm_upstream, and 503 when it can't read the upstream catalog. For a workload deny entry, an unknown name returns 409 unknown_llm_upstream, including when AIControls can't read the upstream catalog; only names that the write adds are checked, so a deny that still names a removed upstream keeps saving.
  • At evaluation, an upstream that isn't configured matches nothing, so a grant through it fails closed.
  • A restrictive binding (a deny-mode binding) or a workload deny entry with upstreams also matches a call whose upstream isn't resolved yet, so it doesn't miss a call it would block.

Model selectors work for every subject type, including derived workload subjects. Team and PAT model scopes and a virtual key's allowed models stay separate ceilings: a model marker never widens them.

Generation fence​

Every write that can change a workload's derived access advances the workload access generation. AIControls reads the generation with each workload's snapshot, and records the generation that each compiled policy set reflects.

When a request's snapshot generation is newer than the compiled set, the replica reloads its policies once. If the reloaded set is still behind, the request is denied with workloadaccess:generation-stale, which no exception can lift. Only requests that carry a non-empty accessPolicy are fenced. Requests from unconfigured identities keep the existing last-good behavior. If a replica can't read the generation when it first loads policies, it treats the applied generation as unknown and fences workload requests until a load succeeds.

These writes advance the generation:

  • A registration create, edit, or delete, or a containment change
  • A Workload Blueprint edit, or a Runtime Binding edit
  • A workload override edit
  • Any Access Binding create, update, or delete
  • Any Toolset create, update, or delete, because a Toolset changes the selectors of every binding that references it

If a workload deny policy, or a binding policy that carries a derived workload subject, fails to compile, the replica can't prove the workload's access is complete, so every workload request is denied with workloadaccess:generation-stale until a compilable set loads. The metric aicontrols_workload_access_generation_stale_total counts these denials by plane.

See also​