FeaturesAgent CardsSigned Agent Cards

Signed Agent Cards

Sign the .well-known/agent-card.json response so clients can verify the card hasn’t been tampered with in transit. Signing is optional — every card ships unsigned by default.

What signing gives you

  • Authenticity — Clients prove the card was issued by your org, not by a man-in-the-middle.
  • Integrity — Any byte-flip in the card invalidates the signature.
  • Discoverable trust — The public key set lives at a sibling /.well-known/jwks.json so any verifier can find it without out-of-band setup.

The signed response uses JWS Compact Serialization (RFC 7515) with Content-Type: application/jose. Unsigned cards continue to return JSON exactly as before — clients that don’t understand JWS keep working.

Supported algorithms

AlgorithmKey typeNotes
ES256EC P-256Default. Small keys, fast verification.
RS256RSA 2048Widely supported by older verifiers.
EdDSAEd25519Modern, compact, fast.

Enabling signing

  1. Open the agent card and switch to the Security tab.
  2. Scroll to the Card signing section.
  3. Pick an Algorithm.
  4. Choose a key source:
    • Generate a key — A²D mints an (alg, kid) pair and stores the private JWK encrypted at rest.
    • Upload a key — Paste a public JWK and the matching private JWK in the form. Use this when your org’s KMS or HSM owns the private material elsewhere.
  5. Click Enable signing.

From this point on, every fetch of .well-known/agent-card.json returns a JWS signed with the active key. The unsigned JSON is still embedded inside the JWS payload — verifiers extract it after checking the signature.

Private JWKs are encrypted with CREDENTIALS_ENCRYPTION_KEY (AES-256-GCM, the same envelope-encryption pattern used elsewhere on the platform). They never leave the server: API responses about a card’s signing config strip the private material before returning.

How clients verify

A verifying client follows a fixed three-step recipe:

  1. Fetch https://example.com/api/platform/<id>/a2a/.well-known/agent-card.json. If the response is application/jose (or matches the JWS compact regex ^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$), treat the body as a JWS.
  2. Decode the JWS protected header to read kid and alg.
  3. Fetch the sibling JWKS at https://example.com/api/platform/<id>/a2a/.well-known/jwks.json, find the key whose kid matches, and verify the signature.

The Protocol Tester does this automatically: when discovery sees a JWS card, it fetches the JWKS through the platform proxy and shows a badge next to the card name:

  • Signed (green) — Verification succeeded.
  • Bad signature (red) — A key matched but the signature didn’t verify. Treat the card as untrusted.
  • Unverified (amber) — JWKS unreachable or no matching kid. The card payload is still readable but the chain of trust is broken.

Rotating keys

Rotation lets you switch which key signs new responses without invalidating existing trust:

  1. Open the card’s Security tab → Card signing.
  2. Click Rotate.
  3. A²D generates a new (alg, kid) pair, marks it active, and keeps the previous key in the JWKS so verifiers caching the old kid keep working until they refresh.

The list of keys on the form shows each kid plus its createdAt and (if revoked) revokedAt.

Revoking a key

If a private key was exposed, click Revoke next to that kid:

  • The key is marked revoked and removed from the published JWKS immediately.
  • If the revoked key was the active signing key, the card stops signing until you click Rotate to mint a new one (or Disable signing to fall back to unsigned).

Testing failure modes

To exercise client error handling without burning a real key, flip the Mock invalid signature switch in the signing UI. The card will still return a JWS, but the last byte of the signature is corrupted so any verifier will reject it. The Protocol Tester shows Bad signature as expected.

Use this to:

  • Verify your client surfaces a clear error rather than silently accepting tampered cards.
  • Demo what a compromised card looks like without taking real keys offline.

JWKS endpoint

The public key set is published at:

GET /api/platform/<id>/a2a/.well-known/jwks.json

Response shape:

{
  "keys": [
    {
      "kty": "EC",
      "crv": "P-256",
      "x": "...",
      "y": "...",
      "kid": "k_2026...",
      "alg": "ES256",
      "use": "sig"
    }
  ]
}

Revoked keys are filtered out. Disabled or never-enabled cards return { "keys": [] }.

Disabling signing

Click Disable signing in the form. The card immediately reverts to unsigned JSON; existing keys stay in the config (still encrypted) so you can re-enable later without re-uploading.

Next Steps


Signing is opt-in: enable it only when you need provable authenticity, and rotate keys when something looks off.