The gap between what an agent claims and what the platform can verify is a real attack surface, and it grows with every new agent you onboard. As agents increasingly discover and call each other at runtime, protocols like Agent2Agent (A2A) have introduced a useful building block for addressing this: the Agent Card. In the same way a model card gives us useful information about a large language model, so does the agent card for an agent. Among that useful metadata are the skills of the agent (what’s it meant to do), its capabilities (e.g., processing an image file), and, finally, a specification for a cryptographic signature.
But nothing inherently ties that metadata back to the workload actually serving it at runtime. This blog post discusses how we can make use of that signature in order to answer a critical question:
“Is this agent really who it claims to be?”
There are several ways to close this gap: custom admission webhooks, dedicated signing services, policy engines, and more. In this post, we share an approach that is relatively lightweight and elegant, particularly in architectures that already use SPIFFE/SPIRE, a framework for issuing and verifying cryptographic identities for workloads, for workload identity. On OpenShift, that foundation is provided by Red Hat’s Zero Trust Workload Identity Manager, which manages the SPIRE lifecycle and workload attestation out of the box, giving every pod a cryptographically verifiable identity from the moment it starts.
Note: Red Hat’s Emerging Technologies blog includes posts that discuss technologies that are under active development in upstream open source communities and at Red Hat. We believe in sharing early and often the things we’re working on, but we want to note that unless otherwise stated the technologies and how-tos shared here aren’t part of supported products, nor promised to be in the future.
In our ongoing work around Kagenti, a Kubernetes operator for managing AI agents and their discovery metadata, we’ve been building on that identity layer to tackle agent trust across the full lifecycle: deployment, discovery, and runtime communication.
This post lays out a practical three-step approach:
- Bind each Agent Card to an approved Kubernetes workload identity.
- Make Agent Cards tamper-evident with digital signatures over a canonical JSON form.
- Enforce who can call which agent at runtime using mesh identity and authorization.
The evolution of discovery: from static services to dynamic agents
For the last decade, most Kubernetes service discovery has been relatively static: you discover where a service lives (a DNS name or IP) and decide whether traffic is allowed. Security often collapses to a binary question:
“Can Service A talk to Service B?”
AI agents introduce a fundamental shift. Agents don’t just exist at an endpoint—they expose dynamic capabilities. Increasingly, they need to discover each other not by a fixed name, but by skill:
“Find me an agent that can analyze financial spreadsheets.”
To support this kind of capability-based discovery, protocols like Google’s Agent2Agent (A2A) define a way for agents to advertise skills and connection details using an Agent Card. Conceptually, it’s a digital business card.
The business card problem
In the physical world, anyone can print a business card claiming to be a doctor, but that doesn’t make them one. Similarly, in a Kubernetes cluster, what stops a malicious workload from publishing an Agent Card that claims, “I am the Payment Processor”?
If we rely purely on self-described Agent Cards for discovery, we open the door to impersonation, redirection attacks, and capability spoofing. That’s why agent identity is harder than just blocking a port. It’s a chain of trust that must hold from the moment an agent is deployed to the moment it receives a request.
A three-step, defense-in-depth approach
When we talk about agent identity, it’s tempting to look for a single silver bullet. In practice, the security boundary is spread across the lifecycle. In Kagenti, we treat this as three distinct questions.
| Phase | The Question | The Goal |
| Deployment | Is this workload allowed to serve this Agent Card? | Prevent impersonation |
| Discovery | Is the card authentic and untampered? | Prevent spoofing/modification |
| Runtime | Is the caller allowed to talk to this agent? | Prevent unauthorized calls |
Let’s look at how we implement these layers in practice.
Step 1: Bind identity to the workload
The first vulnerability is a mismatch between the document (the Agent Card) and the infrastructure (the Pod). An Agent Card can claim to be anything, so the platform needs to vouch for it.
What we do today
In Kagenti’s current implementation, we bind an Agent Card to a Kubernetes workload using a policy-based identity convention. We derive an expected identity using the SPIFFE identifier format:
spiffe://<trust-domain>/ns/<namespace>/sa/<serviceAccount>
We then enforce a simple rule: Only workloads running as the expected Kubernetes namespace + service account may serve this specific Agent Card.
This prevents look-alike agents from publishing a convincing copy of a legitimate card under a different service account.
What happens if the binding fails?
Enterprise teams need safety valves. When Kagenti detects a binding mismatch, it records the failure in the AgentCard status and supports gradual adoption.
- Audit mode: Log warnings and record events for visibility, but continue to trust the card for discovery.
- Strict mode: Mark the card as untrusted and, when network policy enforcement is enabled, isolate the workload’s traffic.
This binding isn’t just a naming convention. At deployment time, an init-container fetches an X.509 identity certificate (SVID) from SPIRE and uses it to sign the Agent Card. The operator then verifies the certificate chain against the SPIRE trust bundle and extracts the SPIFFE ID from the signing certificate, closing the loop between the card and the workload that produced it.
Where we’re heading
SPIRE tells us who is running right now, but it doesn’t tell us how the Agent Card got here. A compromised CI pipeline could inject a subtly altered card into a legitimate workload, and the SPIFFE binding would still pass because the workload itself is genuine.
To close that gap, we’re exploring supply-chain provenance using Sigstore. The idea is to sign Agent Cards at build time with keyless, short-lived certificates tied to the CI identity that produced them. A verifier can then check not just that the card matches the running workload, but that it was built by a trusted pipeline and hasn’t been tampered with since.
This creates two complementary layers of trust: SPIRE answers the question “Is this workload who it claims to be?” and Sigstore answers the question “Was this card published through a process we trust?” Together, they make impersonation significantly harder; an attacker would need to compromise both the runtime identity and the build pipeline.
Step 2: Make the Agent Card tamper-evident
Even if the workload is legitimate, the Agent Card must be retrieved over the network. That introduces the risk of tampering:
- A middleman modifies an endpoint URL to point to a malicious server.
- A card is copied and subtly edited inside the cluster.
To address this, we make the Agent Card tamper-evident using JWS (JSON Web Signature) with X.509 certificate chains:
- Serialize the Agent Card to a canonical JSON form (stable key ordering, empty fields removed, minimal whitespace).
- Sign the canonical representation using the workload’s SPIRE-issued private key (RSA or ECDSA).
- Embed the signature in the card using a JWS with an x5c header, which carries the full certificate chain inline:
{
"signatures": [
{
"protected": "<base64url header: alg, kid, x5c certificate chain>",
"signature": "<base64url signature value>"
}
]
}
The x5c header is what makes this self-verifying: a verifier can validate the signature, check the certificate chain against a trust bundle, and extract the signer’s SPIFFE identity, all from a single signature block. If a single character changes after signing, whether inside the cluster or on the wire, verification fails, and clients can reject the card immediately.
Step 3: Enforce identity at runtime
Steps 1 and 2 ensure the right workload is serving the card and the metadata hasn’t been manipulated. But there’s still a critical question:
Who is allowed to call the agent?
If a caller can reach an agent endpoint just by discovering its URL, you still have a gap.
What we do today
Kagenti includes an optional NetworkPolicy controller that uses the verification results from Steps 1 and 2 to enforce network-level isolation. Agents with verified signatures receive a permissive policy; unverified agents are restricted to control-plane traffic only. This is a coarse-grained but effective first layer.
Where we’re heading
For fine-grained caller authorization, the service mesh becomes a natural next layer. With a mesh like Istio configured for STRICT mTLS, every request is encrypted and cryptographically bound to a caller identity.
That enables authorization policies based on workload identity:
“Only callers with verified identity in the finance-team allowlist can reach the payment-agent.”
This shifts security from perimeter defense (anyone inside the cluster is trusted) to zero trust (every request must prove who it is).
Bringing it all together: a chain of trust
By layering these controls, we move beyond the fragility of trusting the network to a robust chain of trust. Critically, the layers reinforce each other: the JWS signature from Step 2 carries the SPIFFE identity from Step 1 inside its certificate chain, so verifying the signature simultaneously proves both integrity and provenance.
- Binding (Deployment): Prevents an attacker from deploying a look-alike agent.
- Integrity (Discovery): Prevents discovery metadata from being silently manipulated.
- Enforcement (Runtime): Prevents unauthorized access to the running service.
Together, these steps help solve the business card problem in a way that’s practical for Kubernetes environments.
What’s next
We’re actively developing this approach in the open as part of Kagenti. If you’re building agent systems on Kubernetes and thinking about discovery and identity, we’d love your feedback especially on:
- Key distribution and rotation for Agent Card verification.
- How you’d like failure modes to behave (audit vs. strict).
- What identity signals are most useful for runtime authorization.

