---
title: Give an agent its own identity | Keycard
description: An agent proves what it is using a token its runtime already issues, so a compromised host yields nothing reusable and the agent can hold entitlements of its own.
---

Some agents do work that belongs to them rather than to a person. Those need an identity of their own, and they should be able to prove it without holding a secret that works anywhere it is copied to.

## The problem

An agent that acts on its own behalf has to authenticate somehow. In practice that means a client secret in an environment variable, or a long-lived API key in a config file.

A secret in a config file is portable in the worst way. Copy it and it works. Anything that can read the process environment, the deployment manifest, the CI log, or a heap dump gets a credential that keeps working from anywhere, for as long as nobody rotates it. Nothing about the credential ties it to the machine it was meant for.

Rotation is the usual answer and it does not scale to agents. Every rotation is a coordinated change across every place the secret was copied to, and the copies are not tracked, so rotation gets deferred and the secret ages. A credential nobody wants to touch is one nobody revokes either.

The cost shows up in the log as well. If several services share a credential, the system receiving the call cannot tell them apart, so a question like “which service did that” cannot be answered from evidence.

```
flowchart LR
    A[Client secret in config] --> B[Your Agent]
    A -.->|"copied to CI"| C[Build logs]
    A -.->|"copied to a laptop"| D[Developer machine]
    B -->|"works from anywhere"| E[Resource]
```

## When this applies

- The agent’s work is its own rather than something a specific person asked for
- It runs somewhere that can already prove what it is, such as a container platform, a serverless runtime, or a CI job
- You want a compromised host to yield nothing that works elsewhere
- The agent should hold entitlements in its own right, so policy can be written about it

Suppose you run a service that reconciles data between two systems on a schedule. No person triggers it. It needs to reach both systems, and today it does that with two API keys checked into a secrets manager and injected at boot. What you want is for the service to prove it is that service, on that platform, and receive access on that basis.

### When it does not

Where a person triggered the work and the downstream system should see that person, the agent should carry their identity instead. See [Act as the user who asked](/use-cases/act-as-the-requesting-user/index.md).

Where the agent runs somewhere with no way to attest to itself, there is nothing to verify. A platform that cannot issue an identity token for a workload leaves the client secret path as the option, and that is worth knowing before you scope an evaluation.

Where the downstream system only accepts a static credential, the secret still has to exist somewhere. Keycard can hold and broker it rather than each agent holding a copy, which changes who can account for it rather than removing it.

## The solution

The mechanism is smaller than it looks. Your agent’s runtime already issues it a short-lived identity token. Keycard verifies that token against the issuer’s published keys and matches it to a credential registered on the [Application](/concepts/applications/index.md), pinned to the exact subject.

That pin is what makes it non-portable. The credential is not a value the agent holds, it is a statement about which issuer, and which subject within that issuer, is allowed to authenticate as this Application. Another workload on the same platform presents a different subject and does not match.

Because the trust is expressed as an issuer and a subject rather than as a platform integration, anything that publishes an OpenID Connect discovery document and a key set can be an identity source. The platforms with worked guides are examples of the mechanic rather than the limit of it.

## How it works

```
flowchart LR
    A[Your runtime] -->|"Issues a short-lived identity token"| B[Your Agent]
    B -->|"Presents it to Keycard"| C[Keycard]
    C -->|"Verify signature against the issuer's keys"| C
    C -->|"Match issuer + subject to the Application"| C
    C -->|"Scoped credential"| B
    B -->|"Calls with that credential"| D[Resource]
```

1. The runtime issues the agent an identity token. On a container platform this is usually a file the platform mounts and rotates; on others it comes from a metadata endpoint.
2. The SDK reads that token and presents it to Keycard as the Application’s authentication credential.
3. Keycard verifies the signature against the issuer’s published keys, then matches the issuer and subject against the workload identity credential registered on that [Application](/concepts/applications/index.md).
4. [Policy](/concepts/policies/index.md) evaluates the request, and an allowed one yields a credential scoped to the Application and the [Resource](/concepts/resources/index.md) it asked for.
5. The agent calls the Resource. The [Audit Log](/admin/audit-log-and-sessions/index.md) records the issuance against that Application.

Local development uses a client secret, and that is fine

Nothing on a developer laptop can attest to a deployed workload, so local development authenticates with a client ID and secret. The SDKs prefer an explicitly configured client secret over workload identity, which is convenient locally and worth knowing when a deployed service unexpectedly authenticates the wrong way. If a deployment is not picking up its workload identity, check whether those variables are set in that environment.

## What you get

There is no secret to steal from the host. What the agent holds is a token its platform minted for it, short-lived and pinned to that workload, and reading it off the machine gets you something that expires and that will not match from anywhere else.

Rotation stops being a task. The platform reissues the identity token on its own schedule and nothing has to be coordinated across the places a secret was copied to, because it was never copied.

The agent becomes something policy can talk about. It has its own identifier, so a rule can permit this service and not that one, and the audit trail names which Application acted rather than showing a shared principal.

## What you take on

You register the trust once, and precisely. Someone has to record the issuer and the exact subject on the Application, and the subject string is platform-specific and easy to get slightly wrong. Getting it wrong fails shut, which is the right direction, and it is still a step that needs care.

The runtime has to be able to attest. This works because your platform issues an identity token. Where it cannot, the client secret path is what remains, and no amount of configuration changes that.

Coverage is per Application. Each agent that should be separately identifiable is separately registered, which is the point and is also work that scales with the number of agents.

An entitlement now belongs to the agent. Once a service holds access in its own right rather than borrowing a person’s, nothing narrows it when a particular task did not need all of it. Scope it in policy to what the job actually does.

## Common questions

### What stops another workload on the same platform authenticating as our agent?

The subject pin. Keycard matches both the issuer and the subject in the presented token against what is registered on the Application, so a different workload on the same platform presents a different subject and does not match. The [Deploy to Render without Secrets](/guides/deploy-to-render-without-secrets/index.md) guide shows the subject format for one platform and is a good model for reading any of them.

### Which platforms does this work on?

The mechanic is an OpenID Connect issuer plus a subject, so the question is whether your platform issues an identity token for a workload and publishes keys to verify it. Kubernetes and EKS mount one as a file, Google Cloud runtimes expose one through the metadata server, and Fly and Render each have a documented path. Rather than treating that as a supported list, check whether your platform issues workload tokens, because that is what decides it.

### Is this the same as the agent having a user?

No, and keeping them separate matters for audit. An Application acting as itself is one principal. An Application acting for a person is a chain, and the record shows both. Where an agent does both kinds of work, those are two different flows against the same registration, and reviewers usually want to see them distinguished.

### How do we stop it if something goes wrong?

Revoking what the Application may obtain stops the next issuance rather than reaching a credential already in flight, so containment is bounded by the current credential’s remaining lifetime. [Revoke once, access stops](/use-cases/revoke-once/index.md) covers what each mechanism actually does and where the gaps are.

## Where else this applies

- A scheduled reconciliation service reaching two systems, with a policy for each
- A CI job that needs a credential for the duration of a build and nothing afterwards
- A background enrichment worker that reads one Resource and writes another
- A sub-agent in a multi-agent system that should be separately identifiable from the agent that called it

The shape is the same each time. Something runs without a person attached, it can prove what it is to its own platform, and the credential it receives should be tied to that proof rather than to a value someone copied into a config file.

## Related

- [Run Apps Without Static Secrets](/guides/run-apps-without-static-secrets/index.md) is the build walkthrough for this pattern
- [Deploy to Render without Secrets](/guides/deploy-to-render-without-secrets/index.md) is a worked example including the subject format
- [Grant Agent Access to APIs](/guides/grant-agent-access-to-apis/index.md) covers agents that act independently of any person
- [Applications](/concepts/applications/index.md) explains registration and the credentials an Application can hold
- [Providers](/concepts/providers/index.md) covers the identity and access providers a Zone connects to
- [Credentials](/concepts/credentials/index.md) explains issuance and what a credential is scoped to

[Build this ](/guides/run-apps-without-static-secrets/index.md)Run a service whose own identity authorizes every call, with no static secret in its configuration
