Skip to content

All posts

AIKubernetes

How to give custom AI agents their own service identity

Ritesh Patel

AI: How to give custom AI agents their own service identity

A custom AI agent needs its own service identity: a named account with its own credentials and grants, not a shared service account or a copied API key. In Kubernetes, that means one service account per agent, bound to a narrow Role. For agents that call GitHub, use a GitHub App with short-lived installation tokens.

Checked against the Kubernetes service account and token documentation and the GitHub Apps authentication documentation, both fetched on 2026-11-08. The manifests below have not been run on a cluster yet.

This post covers the identity model and the GitHub side. For Roles, bindings and the namespace default account in detail, read the RBAC for AI agents in Kubernetes post.

Choose the identity type first

Two questions decide the identity. Does the agent act for itself or for a person? Does it call the Kubernetes API, or an outside service? The table below sets out the options and the lifetime of each credential.

Option Acts as Credential and lifetime Use it when
Kubernetes service account The workload that runs the agent Projected token, rotated by the kubelet The agent calls the Kubernetes API or an in-cluster service
GitHub App, installation token The app itself Installation access token, expires after 1 hour Unattended automation, such as a bot that opens pull requests
GitHub App, user access token A specific user, within that user’s permissions Issued after the user authorizes the app The agent must do only what that user could do
Static token or shared API key Whoever holds the string Long-lived; a disclosed bearer token can be misused Avoid it

GitHub’s documentation says an installation token attributes activity to the app, while a user access token attributes it to the user. Pick the row that matches who should answer for the action.

Give each agent its own service account

A pod that names no service account runs under the namespace’s default account. Kubernetes gives that account no permissions beyond default API discovery. Any role bound to default still applies to every pod that falls back to it, so an agent that runs there inherits whatever other workloads were given.

  1. Create a named service account for each agent.
  2. Bind a Role to it in one namespace, as the RBAC post shows.
  3. Set spec.serviceAccountName on the pod. You can only set this field when the pod is created.
  4. Turn off token mounting for an agent that never calls the Kubernetes API.

Source: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ (fetched 2026-11-08).

apiVersion: v1
kind: ServiceAccount
metadata:
  name: release-agent
  namespace: agents

When a pod sets automountServiceAccountToken and its service account sets it too, the Pod spec wins. A pod can therefore opt out even when its account does not.

Use short-lived tokens, and check them the right way

Since Kubernetes v1.22, the kubelet requests tokens through the TokenRequest API and mounts them as a projected volume. You set the audience and the lifetime in the Pod spec. The kubelet refreshes the token before it expires, and it requests a rotation once the token is older than 80% of its lifetime or older than 24 hours. Without an explicit lifetime, the token lasts one hour, per the service account admin page.

The application has to reload the file. Load it on a schedule, for example every five minutes, rather than tracking the exact expiry time.

Source: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ (fetched 2026-11-08), names changed from the docs’ example.

apiVersion: v1
kind: Pod
metadata:
  name: release-agent
  namespace: agents
spec:
  serviceAccountName: release-agent
  containers:
    - name: agent
      image: registry.example.com/release-agent:1.0
      volumeMounts:
        - mountPath: /var/run/secrets/tokens
          name: agent-token
  volumes:
    - name: agent-token
      projected:
        sources:
          - serviceAccountToken:
              path: agent-token
              expirationSeconds: 7200
              audience: release-api

When your own service receives the token, the Kubernetes concepts page recommends the TokenReview API for validating it, over OIDC discovery. The API server then checks the token against the object it was bound to. A TokenReview for the token above looks like this:

Source: https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/ (fetched 2026-11-08).

apiVersion: authentication.k8s.io/v1
kind: TokenReview
spec:
  token: <token presented by the agent>
  audiences:
    - release-api

Narrow the GitHub installation token

A GitHub App gets an installation access token by sending a JWT that identifies the app to the installation endpoint. The token expires after one hour. The request can include a repositories list and a permissions object. Both narrow the token, and neither can grant more than the app already has.

Source: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app (fetched 2026-11-08).

curl --request POST \
  --url "https://api.github.com/app/installations/INSTALLATION_ID/access_tokens" \
  --header "Accept: application/vnd.github+json" \
  --header "Authorization: Bearer JWT" \
  --header "X-GitHub-Api-Version: 2026-03-10" \
  --data '{"repositories": ["release-notes"], "permissions": {"contents": "read"}}'

Replace INSTALLATION_ID with the installation’s ID and JWT with the app’s JSON web token. If the request omits repositories, the token can reach every repository the installation was granted. If it omits permissions, the token keeps every permission the app has. Set the smallest set the agent needs.

Details in the fine print that change the design

Several details are easy to miss, and each one changes how you set up an agent.

  1. RBAC has no deny rules. Permissions are purely additive, so every binding that names an agent’s account applies to every pod that uses that account.
  2. Deleting a pod invalidates its bound tokens at the API server. Their expiry time does not change, and an offline validator that does not call TokenReview can still accept them until they expire. Kubernetes provides no separate revoke step for a TokenRequest token, so deleting the pod is the control you have.
  3. The Pod’s service account is fixed at creation. You cannot change spec.serviceAccountName on a running pod, so a new identity means a new pod.
  4. Secret-based tokens do not rotate. They do not expire, and Kubernetes no longer recommends them.
  5. Installation tokens default to everything. Without repositories or permissions, the token gets all of the app’s permissions.

Record who the agent acted for

Neither Kubernetes nor GitHub records the person who asked an agent to act, based on what these pages describe. By default, user accounts are not objects in the Kubernetes API server, so the delegating person never appears in a service account token. That record has to live in your own audit system.

Write one record for each action, with these fields:

  1. The agent’s name, and its service account or GitHub App.
  2. The task or ticket the agent was working on.
  3. The person who started the task.
  4. The tool call and the target it changed.
  5. The time, in the same clock as your other audit records.

A checklist before an agent goes live

Work through this list before the first production run. Each item maps to a section above.

  • The agent has its own service account or GitHub App, shared with no other workload.
  • No grant is bound to the namespace’s default account.
  • Token lifetime is set, and the application reloads a rotated token.
  • Your service validates tokens with TokenReview and checks the audience.
  • GitHub installation tokens name their repositories and permissions.
  • Every action writes a record that names the person who started the task.
  • Bindings and app permissions are reviewed on a fixed schedule, since grants do not expire.

Where to go next

Start with What is agent identity? if you need the definition, and read How to map AI agents and humans to one identity model with OIDC for sessions that act for a signed-in person. For the wider picture, see the governing AI agents hub. The AIControls platform page covers the product side. This post makes no product claims.

Frequently asked questions

Can a custom AI agent use a GitHub App instead of a service account?

Yes, when the agent calls GitHub. A GitHub App can authenticate as itself and request an installation access token, which expires after one hour. Use that token when the app should be accountable for the action. Use a user access token when the agent must stay within one user's permissions. Narrow the installation token with the repositories and permissions parameters. If you leave them out, it receives every permission the app has and every repository the installation can reach.

Is a Kubernetes service account enough identity for an AI agent?

It is the right base for an agent that runs in a cluster and calls the Kubernetes API. The account names the workload, carries its credentials and takes RBAC bindings. It does not record which agent inside the pod acted, the task it was doing or the person who asked for it. Keep those in your own audit records. Give each agent its own account, so at least the grants can differ from one agent to the next.

Should an agent mount a long-lived token Secret?

The Kubernetes docs say these Secret-based tokens do not expire or rotate, and that the method is not recommended anymore, especially at scale, because of the risks of static, long-lived credentials. Use a projected token with an audience and an expirationSeconds value instead. Make sure the application reloads the file when the kubelet rotates it.

How should a service check an agent's Kubernetes token?

Use the TokenReview API, which the Kubernetes project recommends. The API server checks the token against the objects it is bound to, so a token stops validating when its Pod is deleted, even before its expiry. OIDC validation does not make that check, so a token can look valid until it expires. Have your service check that the token's audience matches the audience it accepts.

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.