
Sovereign AI
AI Governance Models: Who Owns the Model-Swap Gate
Centralised, federated or hybrid AI governance only works on owned inference when it maps onto who may edit the admission policy that approves a model swap.
Search for AI governance models and the results converge on three diagrams. A centralised model puts one function in charge of every AI decision. A federated model pushes decisions out to business units. A hybrid model sets standards centrally and lets teams execute. None shows the mechanism by which a decision taken in that committee changes what a GPU serves on Tuesday afternoon.
For an organisation consuming AI as a procured service, that gap is harmless: the committee's lever is the contract, and procurement pulls it. For a team running inference on its own cluster, the lever is different. Three product teams share one GPU pool; one of them changes a single field in a manifest and different weights start answering requests. Whoever can approve or block that change is the governance model, whatever the org chart says.
Three org charts and the decision none of them executes
The people who own AI governance today are mostly not the people who hold cluster-admin. The IAPP and Credo AI AI Governance Profession Report 2025 — an IAPP governance survey run in spring 2024 with more than 670 respondents across 45 countries and territories — found privacy (22%) and legal and compliance (22%) to be the functions most often given primary responsibility for AI governance, followed by IT (17%). Half of AI governance professionals are typically assigned to ethics, compliance, privacy or legal teams.
The accompanying infographic adds the reporting lines: most AI governance departments report to the general counsel or head of legal (23%), the CEO (17%) or the CIO (14%). It also reports that 77% of organisations are working on AI governance, a figure it puts at over 85% among those also using AI. Governance is being staffed; it is being staffed in legal and privacy.
Legal and privacy are the right owners for questions of purpose, risk tier and contractual exposure. The problem is narrower: a centralised, federated or hybrid org chart drawn around those functions has no arrow into the cluster. On owned infrastructure, the arrow has to be drawn explicitly, and it ends at an object with an RBAC rule on it.
The decision is a model swap, and the law cares who makes it
Most self-hosted inference is not high-risk under the EU AI Act; where it is, a weight swap is the change the Act is written around. Article 3(23) of the AI Act defines a substantial modification as a change after placing on the market or putting into service that was not foreseen in the initial conformity assessment and that affects compliance with the Chapter III Section 2 requirements or modifies the assessed intended purpose. Whether a swap meets that test depends on what the original assessment foresaw.
When one does, Article 43(4) is blunt: a high-risk system that has already been through conformity assessment "shall undergo a new conformity assessment procedure in the event of a substantial modification, regardless of whether the modified system is intended to be further distributed or continues to be used by the current deployer." The exemption is narrow — changes pre-determined by the provider at the initial assessment and recorded under Annex IV point 2(f), for systems that continue to learn after deployment.
Article 11(1) (Section 2, postponed as below) requires the technical documentation to be kept up to date, and Annex IV asks for things a swap changes directly: the version of the system "reflecting its relation to previous versions" in point 1(a), and in point 2(a), where relevant, recourse to pre-trained systems or tools provided by third parties and how the provider used, integrated or modified them. An approved swap is usually a documentation event, and the gate is where that event can be captured rather than reconstructed later.
The Digital Omnibus, Regulation (EU) 2026/1744, replaces Article 113, third paragraph, point (c): Chapter III, Sections 1, 2 and 3, except Article 6(5), apply from 2 December 2027 for systems high-risk under Article 6(2) and Annex III, and from 2 August 2028 for those under Article 6(1) and Annex I. Article 43 sits in Section 5, outside that postponement. The role question — when a deployer's modification makes it a provider under Article 25(1)(b) — is covered in the EU AI Act summary for deployers and in the on-prem high-risk compliance path. Articles 17(1)(m) and 26(2) are Section 3 text on that postponed schedule, not duties already in force. If the organisation becomes a provider, Article 17(1)(m) will require its quality management system to include "an accountability framework setting out the responsibilities of the management and other staff." Even as a deployer, Article 26(2) will require human oversight assigned to people with "the necessary competence, training and authority." Authority over a model swap is a named permission, or it is nothing.
The gate is four objects, so the RACI has four rows
Kubernetes ValidatingAdmissionPolicy, stable since v1.30 and so present on every release still in support, does not ship as one object. The policy holds the CEL rules. A ValidatingAdmissionPolicyBinding "links the above resources together and provides scoping", and ties the policy by name to a parameter resource via paramRef. The binding also chooses the validation actions: Deny rejects the request, Warn returns a warning to the client, and Audit includes the failure in the API audit event, where auditAnnotations can add further detail.
Those objects live at different scopes. The policy and the binding are cluster-scoped. A parameter object can be namespaced — a ConfigMap is allowed — and when the binding's paramRef specifies a namespace, the control plane searches for parameters only in that namespace. Kubernetes RBAC follows the same split: a Role always sets permissions within one namespace, while a ClusterRole is non-namespaced and can grant access to cluster-scoped resources. Two scopes let several parties own the gate without sharing a credential.
That is the bridge NIST asks for. The AI Risk Management Framework is voluntary, but its GOVERN 2.1 asks that roles, responsibilities and lines of communication for mapping, measuring and managing AI risk be "documented and are clear to individuals and teams throughout the organization," and GOVERN 2.3 puts responsibility for AI risk decisions on executive leadership. A RACI written against admission objects is documentation that the cluster enforces. The proposed split below is a hybrid model — it is this article's design, not observed practice:
- Policy (the CEL rule). Responsible: platform team. Accountable: platform lead. Consulted: AI governance function, on what the rule checks. Product teams: informed.
- Binding (scope and actions). Responsible: platform team. Accountable: platform lead. Consulted: governance, before any change from Deny to Warn. Product teams: informed.
- Parameter (the approved-model list). Responsible: AI governance function. Accountable: the executive owner named under GOVERN 2.3. Consulted: platform, on digest and registry format. Product teams: request entries.
- Exception (the way past the rule). Responsible: governance approves, platform applies. Accountable: executive owner. Product teams: request, never write.
- Namespace labels and engine configuration. Responsible and accountable: platform. Nobody else holds write access.
Read against the diagram, the three org charts stop being abstract. Centralised means one group writes policy, parameter and exceptions; it works until that group becomes the bottleneck for every deploy. Federated means each product namespace holds its own parameter object — which paramRef supports, because an empty namespace with a namespaced paramKind uses the request's own namespace — and it works only if someone is accountable for what those lists contain. Hybrid pins paramRef.namespace to one governance namespace.
Native ValidatingAdmissionPolicy: the hybrid model in YAML
The manifest below assumes KServe, a CNCF incubating project, serving models from OCI artefacts. KServe's OCI storage takes an oci:// reference in spec.predictor.model.storageUri, and depends on the modelcar feature, which is not enabled by default — it is switched on with enableModelcar in the storageInitializer block of the inferenceservice-config ConfigMap. KServe's docs advise a specific tag rather than latest; requiring an @sha256: digest is this policy's own, stricter choice, because a tag can be moved after the governance function approved it.
ConfigMap data is a map of strings to strings, and its keys cannot hold a URI. So each entry's key is a human-readable model name and its value is the digest-pinned reference; the CEL tests the value. parameterNotFoundAction: Deny, which then defers to the policy's failurePolicy: Fail, makes a deleted or renamed list fail closed instead of admitting everything.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: approved-model-weights
spec:
failurePolicy: Fail
paramKind:
apiVersion: v1
kind: ConfigMap
matchConstraints:
resourceRules:
- apiGroups: ["serving.kserve.io"]
apiVersions: ["v1beta1"]
operations: ["CREATE", "UPDATE"]
resources: ["inferenceservices"]
variables:
- name: uri
expression: >-
has(object.spec.predictor.model) &&
has(object.spec.predictor.model.storageUri)
? object.spec.predictor.model.storageUri : ''
validations:
- expression: "!has(object.spec.canary) || size(object.spec.canary) == 0"
message: "spec.canary serves a second storageUri this policy does not check; use canaryTrafficPercent"
reason: Forbidden
- expression: "variables.uri.startsWith('oci://') && variables.uri.contains('@sha256:')"
message: "storageUri must be an oci:// reference pinned by @sha256 digest"
reason: Invalid
- expression: "has(params.data) && params.data.exists(k, params.data[k] == variables.uri)"
messageExpression: "'model ' + variables.uri + ' is not on ai-governance/approved-models'"
reason: Forbidden
auditAnnotations:
- key: storage-uri
valueExpression: "variables.uri"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: approved-model-weights
spec:
policyName: approved-model-weights
validationActions: [Deny, Audit]
paramRef:
name: approved-models
namespace: ai-governance
parameterNotFoundAction: Deny
---
apiVersion: v1
kind: ConfigMap
metadata:
name: approved-models
namespace: ai-governance
data:
summariser-v3: "oci://registry.example.com/models/summariser@sha256:e2ef0664d86111575df12cf2a74cbc996dfe3470a35fcee571c0c54a9cdc24ad"
classifier-v7: "oci://registry.example.com/models/classifier@sha256:1ca3a0a6778e0347ff88048a587e869cb7de846c809575c2d727e9a7711bd12d"Three details matter. Deny and Warn cannot be combined in one binding; [Deny, Audit] both rejects and records. The audit annotation lands in the event under the key approved-model-weights/storage-uri — the policy name is prefixed — so a SIEM query can find every swap with a non-empty URI. An InferenceService using a per-framework predictor field instead of model evaluates to an empty URI and is denied. That leaves a second path: KServe v0.20 added a RawDeployment spec.canary list, each entry its own Deployment with its own storageUri. The first validation denies any non-empty spec.canary rather than admit an unchecked model behind an approved one; Serverless canaryTrafficPercent uses the checked field and still works. The gate does not match the separate LLMInferenceService CRD, and refuses oci+native:// and oci+fetch:// URIs — fail closed, not a bypass.
The parameter is only as governed as its RBAC. The governance group gets update on exactly one named ConfigMap in its own namespace. The platform group writes the cluster-scoped admission objects and can read that ConfigMap but not change it — the read is required, because creating a binding whose paramRef names the ConfigMap needs get on it:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: approved-models-editor
namespace: ai-governance
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["approved-models"]
verbs: ["get", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: approved-models-editor
namespace: ai-governance
subjects:
- kind: Group
name: ai-governance-board
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: approved-models-editor
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: approved-models-reader
namespace: ai-governance
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["approved-models"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: approved-models-reader
namespace: ai-governance
subjects:
- kind: Group
name: platform-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: approved-models-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: admission-policy-admin
rules:
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["validatingadmissionpolicies", "validatingadmissionpolicybindings"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admission-policy-admin
subjects:
- kind: Group
name: platform-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admission-policy-admin
apiGroup: rbac.authorization.k8s.ioOne limit: anyone bound to cluster-admin can still edit the list. RBAC cannot remove authority from the top of the hierarchy; it can only make its use visible. That is why the evidence section below leans on audit events and git history, not on the Role alone.
Kyverno, Gatekeeper and policy-controller: where each one leaks authority
Each policy engine adds an exemption mechanism, and every exemption mechanism is a fifth row in the RACI. The comparison that matters for governance is not syntax but who can write the way around the rule — the same question the Kyverno vs Gatekeeper comparison asks at cluster scale.
Kyverno
Kyverno's ValidatingPolicy "extends the Kubernetes ValidatingAdmissionPolicy type," so the CEL above ports almost unchanged, and NamespacedValidatingPolicy gives the federated model a native object. Its ImageValidatingPolicy verifies signatures and attestations with Cosign or Notary attestors. The authority leak is the PolicyException. Exceptions are disabled by default; enabling them takes the enablePolicyException flag plus exceptionNamespace, which names the namespace exceptions may live in. The container flag reference also accepts *, which accepts exceptions from every namespace — the federated model by accident. The example below targets the policies.kyverno.io/v1 API as documented for Kyverno v1.19:
apiVersion: policies.kyverno.io/v1
kind: PolicyException
metadata:
name: summariser-canary
namespace: kyverno-exceptions
spec:
policyRefs:
- name: approved-model-weights
kind: ValidatingPolicy
matchConditions:
- name: one-inference-service
expression: "object.metadata.namespace == 'team-search' && object.metadata.name == 'summariser-canary'"Set spec.expiresAt on each exception so the bypass ends on its own, and sweep for stale ones. What makes this safe is not the YAML but the RBAC on kyverno-exceptions: if a product team can create PolicyExceptions there, the parameter list is advisory.
OPA Gatekeeper
Gatekeeper's ConstraintTemplate CRD is cluster-scoped (scope: Cluster in its CRD manifest), which suits a platform-owned rule. Exemptions come in two strengths, per its exempt-namespaces documentation: a constraint's own excludedNamespaces, and the Config resource — marked alpha — which excludes namespaces "for all constraints in the cluster." The leak belongs to whoever can edit the Gatekeeper deployment's --exempt-namespace flag or its Config. Gatekeeper can also generate a ValidatingAdmissionPolicy via --default-create-vap-for-templates=true for templates with the CEL engine K8sNativeValidation; VAP management is beta since v3.20.
Sigstore policy-controller
policy-controller answers a different question — was this artefact signed by an approved identity — and multiple matching ClusterImagePolicies are ANDed for admission. By default it validates only namespaces that opt in with the label policy.sigstore.dev/include: "true". Namespace is a cluster-scoped object, so the switch belongs to whoever holds patch on namespaces. In a well-run cluster that is the platform team; the audit script below checks rather than assumes.
Failure modes: when the RACI and the cluster disagree
None of these needs an attacker; each needs only a reasonable engineer under deadline.
- Cluster-admin held by a product team. Easy to inherit on a cluster that began as one team's project. The RACI says governance owns the list; RBAC says the product team does too.
- Mutable tags in the approved list. A tag approved in March can point at different bytes in May. A tag can still be written into the ConfigMap, but the CEL refuses any URI without
@sha256:at evaluation, so it never admits a workload. - A served name that hides the loaded model. vLLM's
--served-model-namesets the name used in the API, defaults to the--modelargument but can differ, and also fills themodel_nametag on its Prometheus metrics. Dashboards keyed on that name report continuity across a swap. Govern the storage reference, never the label — the gap the model-inventory article closes. - Warn left in place after a trial. A binding switched to
[Warn, Audit]during rollout and never switched back records violations and admits them. - An exception namespace the requester can write. The exception path becomes self-service, and the parameter list becomes a suggestion.
What the auditor is shown, and the exit ramp
The evidence for a governance model on owned infrastructure is three artefacts: an RBAC dump proving who can write each object, the version history of the parameter, and the audit events the binding emits. The first is a script, re-runnable in front of an assessor (the auditing identity needs impersonate permission):
#!/usr/bin/env bash
set -euo pipefail
# Abort here if the API server is unreachable, so a "no" below means "not allowed".
kubectl cluster-info >/dev/null
for g in ai-governance-board platform-team team-search; do
echo "== group: $g"
echo -n "update approved-models list: "
kubectl auth can-i update configmaps/approved-models -n ai-governance --as=audit-probe --as-group="$g" || true
echo -n "update VAP bindings: "
kubectl auth can-i update validatingadmissionpolicybindings.admissionregistration.k8s.io --as=audit-probe --as-group="$g" || true
echo -n "create Kyverno exceptions: "
kubectl auth can-i create policyexceptions.policies.kyverno.io -n kyverno-exceptions --as=audit-probe --as-group="$g" || true
echo -n "patch namespace labels: "
kubectl auth can-i patch namespaces --as=audit-probe --as-group="$g" || true
echo -n "cluster-admin equivalent: "
kubectl auth can-i '*' '*' --as=audit-probe --as-group="$g" || true
done
kubectl get validatingadmissionpolicybindings approved-model-weights -o yaml
# Path of the approved list in your own GitOps repo:
git log --follow --format='%h %an %ad %s' -- "${APPROVED_LIST_PATH:-governance/approved-models.yaml}"The audit events need somewhere durable to land that the platform team cannot quietly rewrite — shipping Kubernetes audit logs into a SIEM you operate covers that path. Map the three artefacts to the documentation the frameworks ask for using the artefact-first framework guide and the ISO 42001 clause-to-artefact walk-through.
The exit ramp is built into the choice of API. A ValidatingAdmissionPolicy, its binding and a ConfigMap are upstream Kubernetes objects. Kyverno's ValidatingPolicy extends the same type and Gatekeeper can generate one, so moving between engines rewrites the policy and leaves the parameter untouched. The same holds below the serving layer: if a team leaves KServe for plain pods mounting weights through image volumes, stable since Kubernetes v1.36 and able to mount content from OCI registries, the CEL changes its field path and the approved list stays as it is. Sequencing is in the on-premise AI decision guide, and enforced defaults in golden paths that need enforcement.
The long game: committees reorganise, admission objects persist
Governance charts get redrawn with leadership changes, regulation and fashion. On owned infrastructure each redraw should cost one pull request against a RoleBinding — a group name changes, the gate does not. That is the test of a governance model worth keeping: when the org chart changes, the policy, the binding and the list carry on, and only the names in RBAC move.
§FAQ/Common questions
Frequently asked
What are the main AI governance models?
Centralised, federated and hybrid. Centralised puts one function in charge of every AI decision, federated pushes decisions to product or business units, and hybrid sets standards centrally while teams execute. On infrastructure you run, each reduces to a question of which admission objects — policy, binding, parameter list, exception — each group is permitted to write in RBAC.
Who should own the approved-model list on a shared GPU cluster?
In the hybrid design proposed here, the AI governance function writes the list, held as a parameter object in its own namespace, with an executive owner accountable for it. The platform team writes the policy and binding that enforce the list, and product teams request entries but cannot write them.
Does swapping a self-hosted model trigger a new EU AI Act conformity assessment?
Only for high-risk AI systems, and only when the swap is a substantial modification — a change not foreseen in the initial conformity assessment that affects compliance or the intended purpose. Where it is, Article 43(4) requires a new conformity assessment even if the system stays with its current deployer. Most self-hosted inference is not high-risk.
Can a ValidatingAdmissionPolicy read a ConfigMap as its parameter?
Yes. Set paramKind to v1 ConfigMap and point the binding's paramRef at it by name and namespace. Because ConfigMap data is a string-to-string map, store each approved reference as a value and test membership in CEL, and set parameterNotFoundAction to Deny so a missing list fails closed.
Is the IAPP data saying platform teams should not own AI governance?
No. The IAPP 2025 report shows privacy and legal and compliance most often hold primary responsibility for AI governance, followed by IT. It describes where governance sits today, not where it should sit. The argument here is that whoever owns it needs an explicit, RBAC-backed path into the admission gate on owned infrastructure.
Further reading
- AI Governance Frameworks: What to Build, Not Which to Pick
- AI Governance Best Practices: Inventory What Is Running
- Kyverno vs OPA Gatekeeper: Policy as Code at Cluster Scale
- Platform Engineering: Golden Paths Need Enforcement
- EU AI Act Summary: What Actually Binds a Deployer
- ISO 42001 for Self-Hosted Inference: Clause to Artefact
- On-Premise AI: The Decision Order for the Whole Stack
- Kubernetes Audit Logs Into a SIEM You Operate: Wazuh
Executive Briefing
Thirty minutes to clarify your infrastructure risk
Walk us through your vendor footprint and regulatory constraints. We will tell you honestly where sovereignty creates leverage — and where it does not. No pitch deck. No obligation.