Skip to content

All posts

KubernetesAI

How to give AI agents identity in Kubernetes

Ritesh Patel

Kubernetes: How to give AI agents identity in Kubernetes

Give each AI agent its own named Kubernetes service account, mount a short-lived projected token scoped to one audience, and bind permissions only to that account. When an agent must be identified across trust domains, give its pod a SPIFFE ID from SPIRE, using a workload registration entry on the SPIRE server. Shared accounts cannot show which agent acted.

This guide covers the workload side: how the agent’s pod proves who it is. For how people and agents share one identity provider, see the OIDC guide. For what each identity may do, see RBAC for AI agents in Kubernetes.

Checked against the Kubernetes service account and projected volume documentation and the SPIRE server and plugin documentation, as fetched on 2026-11-06. The SPIRE pages do not state a release version. None of the configuration below has been run on a cluster yet.

Sources:

Why a shared account does not identify an agent

Every namespace has a default service account, and a pod that names no account runs as it. Kubernetes gives default no permissions beyond API discovery, but any grant bound to it reaches every pod in the namespace that does not set its own account. Put three agents on that account and the audit log shows one principal acting three ways. The fix is one named account per agent, so each agent carries its own grants and its own token.

Step 1: Create a named service account for each agent

Bind each agent’s permissions to its own account, as set out in RBAC for AI agents in Kubernetes.

Create one ServiceAccount object per agent in the namespace where the agent runs, named after the agent’s job, such as triage-agent. Set the agent pod to run as that account, and do not reuse it for a second agent. The docs describe automatic recreation for default and say nothing about named accounts, so a named account you delete stays deleted until you recreate it. Keep each agent’s account manifest in version control.

Step 2: Mount a projected token for one audience

Kubernetes issues a short-lived token through the TokenRequest API and mounts it as a projected volume. The volume’s path is relative to the mount point. The audience defaults to the API server’s identifier, and expirationSeconds defaults to one hour, with a minimum of 10 minutes. Set the audience to the service the agent calls, so a receiving service that checks the audience can refuse a token meant for another tool.

spec:
  serviceAccountName: triage-agent
  containers:
    - name: agent
      image: registry.example.com/triage-agent:1.0
      volumeMounts:
        - name: agent-token
          mountPath: /var/run/secrets/tokens
          readOnly: true
  volumes:
    - name: agent-token
      projected:
        sources:
          - serviceAccountToken:
              audience: triage-tool-api   # the service this agent calls
              expirationSeconds: 3600
              path: token

Replace triage-tool-api with your own audience. The volume is declared under spec.volumes and mounted by the container. The path is relative to the mount point, so the container reads the token from /var/run/secrets/tokens/token. The receiving service has to check the token’s audience claim. The docs say the application should always define the audience it accepts and check the token’s audiences against it.

The token is bound, not a copy of a long-lived secret. The docs say the TokenRequest API produces bound tokens, whose lifetime is linked to the client, such as a pod, acting as the account. The API server also checks that the object using the account still exists. Pair that with a short expirationSeconds value, and an agent’s credential expires on its own schedule, not the schedule of a cluster admin.

Step 3: Turn off token automounting where no API call is made

Kubernetes injects credentials for a service account by default. The docs say you can prevent this for a specified service account, or for default, by setting automountServiceAccountToken to false in the Pod specification. Do that for any agent that never calls the Kubernetes API, such as one that only calls an external model or tool endpoint.

An agent that does call the API keeps its token and gets the narrowest Role that works. The mounted token and the Role are separate controls: the token proves who the agent is, and the Role decides what that identity may do. Turning off automounting stops only the automatic credential that the admission controller adds. A projected token that the pod declares itself, such as the one in Step 2, is still mounted if the spec includes it, so an agent that never calls the API should declare no token volume either.

Step 4: Issue SPIFFE IDs with SPIRE across trust domains

SPIRE is the signing authority for SPIFFE IDs. Its server holds the certificate authorities that issue X.509 SVIDs, and its trust_domain setting names the domain it serves. The default X.509 SVID lifetime is one hour. Federation exchanges trust bundles between trust domains, so a second system can verify an agent without calling your cluster. The node agent attests to the server first. Node attestation alone gives that node agent its own SPIFFE ID, not an SVID for the AI workload. The workload gets its SVID through a registration entry and the Workload API, as the sections below describe.

Attest the node with k8s_psat

The k8s_psat node attestor verifies the agent using a projected service account token. Set these keys on the server plugin:

Key What it does
clusters Clusters authorized for attestation. If empty, no cluster is authorized.
service_account_allow_list Namespace-qualified accounts allowed to attest, such as default:blog. Other accounts are rejected.
audience Audience used to validate the token. Defaults to ["spire-server"].
use_pod_uid_for_agent_id Defaults to false. When true, the agent ID uses the UID of the SPIRE agent’s own pod instead of the node UID, with a pod/ prefix in the path. It still names the SPIRE agent, not the workload.

The node agent’s SPIFFE ID then takes the form spiffe://<trust_domain>/spire/agent/k8s_psat/<cluster>/<node UID>. That ID names the SPIRE agent on the node, not the AI workload.

Attest the workload with the Kubernetes attestor

The workload attestor finds the pod through its cgroup membership, then asks the kubelet about it. The docs warn that cgroup membership is only a reliable signal for unprivileged workloads, because root on the node can change it. The attestor produces selectors such as k8s:ns, k8s:sa, k8s:pod-name, k8s:pod-uid and k8s:pod-image. Use digest-based image selectors. The docs note that tag-based selectors can disagree when several tags share one digest.

Register the workload

A workload gets an SVID only when the server holds a registration entry for it. Create the entry with spire-server entry create. Set -parentID to the node agent’s SPIFFE ID, set -spiffeID to the workload’s own ID, and add one -selector per condition, such as k8s:ns:agents and k8s:sa:triage-agent. The workload then requests its SVID from the SPIRE agent on its node through the Workload API.

Compare the identity options

Option Issued by What it names Use it when
Namespace default account Kubernetes The namespace’s default account Never for agents
Named service account and projected token Kubernetes One agent’s account, bound to one audience The agent calls only your cluster’s API and tools
SPIFFE ID with an SVID SPIRE server A path under your trust domain A second system must verify the agent
OIDC identity from your provider Your identity provider A user or client registration Agents act for a signed-in person

Our recommendation, not a sourced rule: use a named account and projected token for every agent, and add SPIRE only when verification has to happen outside the cluster.

A checklist before an agent goes live

  1. The agent runs as its own named service account, and no other workload uses that account.
  2. Each token audience matches the one the receiving service checks.
  3. expirationSeconds is set, and you have confirmed the lifetime the API server actually issues.
  4. Agents that never call the API have automountServiceAccountToken set to false.
  5. Any SPIRE workload selector that names an image uses a digest.
  6. Your audit trail records the account name, plus the task and the person, which Kubernetes does not hold.

Where AIControls fits

AIControls is the Nirmata product for governing AI agents. Its confirmed capabilities include per-user tokens and IdP integration for XAA. The Kubernetes steps in this guide are set up in your own manifests and SPIRE configuration. See AIControls platform for how the product fits this model, and the governing AI agents hub for the wider picture.

Frequently asked questions

Is a named service account enough to give an AI agent identity?

It is the base layer. A named service account gives the agent its own Kubernetes principal, token and grants, so audit entries name the agent, not a shared account. It does not record the task the agent was running or the person it acted for. Those come from your own systems, such as the request that started the agent or the delegated user's session, and you must join them to the Kubernetes audit trail yourself.

Why set an audience on the projected token?

The audience says which service the token is meant for. If you leave it at the default, which is the API server's identifier, a token issued for one tool can be presented to another that accepts the same default. The Kubernetes docs say the receiving application should always define the audience it accepts and check the token's audiences against it. Set one audience per tool the agent calls.

How long does a projected service account token last?

By default, one hour, and the minimum you can request is 10 minutes. The expirationSeconds field sets the expected validity. An administrator can cap the maximum with the API server option --service-account-max-token-expiration, so a longer value in your manifest may be shortened. Check the value the API server actually issues before you rely on a particular lifetime.

When do I need SPIFFE IDs instead of Kubernetes service accounts?

When an agent must be identified outside the cluster that issued its token, or across trust domains. SPIRE issues SPIFFE IDs and X.509 SVIDs, and its federation settings exchange trust bundles between trust domains. Kubernetes service accounts are enough for agents that only call the API server of their own cluster. Add SPIRE when a second system has to verify the agent without calling your cluster.

Ritesh Patel, Founder & VP Product, Nirmata

Ritesh Patel is a founder of Nirmata and leads its products. He writes about governing AI coding agents, MCP security and Kubernetes policy as code.

LinkedIn

Govern AI agents with AIControlsControl what AI agents do, and prove it.
ShareXLinkedIn

Stay up-to-date with the latest in AI, governance, and infrastructure

Subscribe to our free monthly newsletter to get industry news, DevOps tips, and articles right to your inbox on Kyverno, containerization, Kubernetes deployment & management and more.