Skip to content

docs(samples): add Agent Card signing sample - #1198

Open
orangecola wants to merge 1 commit into
a2aproject:mainfrom
orangecola:claude/agent-card-signing-example-dgbwfk
Open

docs(samples): add Agent Card signing sample#1198
orangecola wants to merge 1 commit into
a2aproject:mainfrom
orangecola:claude/agent-card-signing-example-dgbwfk

Conversation

@orangecola

Copy link
Copy Markdown

There was no runnable example for a2a.utils.signing, so this adds one
covering both halves of Agent Card signing.

samples/agent_card_signing.py:

  • Server: generates an ES256 key, signs the Agent Card with
    create_agent_card_signer, serves it via
    create_agent_card_routes(card_modifier=...), and publishes the public key
    as a JWKS document at /.well-known/jwks.json — the URL the signature's
    jku points to.
  • Client: verifies the card with create_signature_verifier passed to
    A2ACardResolver.get_agent_card(signature_verifier=...), resolving keys by
    kid from an allowlist of pinned JWKS URLs, with algorithms pinned to
    ES256.
  • Modes: demo (default, runs both in-process), serve, verify --url.

The default demo mode verifies the genuine card and then shows the three
rejections a verifier exists for:

1. The card served by the agent
   verified card for agent: Signed Card Agent
   protected header: {'alg': 'ES256', 'jku': '.../.well-known/jwks.json', 'kid': 'sample-card-key-1', 'typ': 'JOSE'}

2. A card whose transport URL was rewritten in transit
   rejected as expected: No valid signature found

3. A card with its signature stripped off
   rejected as expected: AgentCard has no signatures to verify.

4. The genuine card, checked by a client that pins other keys
   rejected as expected: No valid signature found

Two non-obvious details are called out in comments, since they bite anyone
copying the pattern:

  • The verifier is a synchronous callable, so fetching the JWKS inside it
    with a sync HTTP client blocks the event loop (it deadlocks against an
    in-process server and reports No valid signature found). The key provider
    does pure in-memory lookups over JWKS prefetched asynchronously.
  • create_agent_card_signer mutates the card it signs, so a per-request
    card_modifier would accumulate signatures. The helper deep-copies first and
    the card is signed once at startup.

The key provider also raises PyJWKError rather than a plain exception, so the
verifier treats one unusable signature as unverifiable and moves to the next
instead of aborting a card that also carries a good signature.

Also adds tests/integration/test_agent_card_signing_smoke.py (runs the demo
on a free port, asserts one acceptance and three rejections) and a
samples/README.md section.

No library code changed — samples, tests and docs only.

Testing

  • uv run pytest tests/utils/test_signing.py tests/integration/test_agent_card_signing_smoke.py tests/client/test_card_resolver.py → 55 passed
  • uv run ruff format --check and uv run ruff check → clean
  • uv run ty check → same 4 pre-existing diagnostics as main (samples are outside [tool.ty.src])

Checklist

Fixes #1197 🦕

There was no runnable example for `a2a.utils.signing`, so add one covering
both halves of Agent Card signing:

- Server: signs the card with an ES256 key via `create_agent_card_signer`,
  wired in through `create_agent_card_routes(card_modifier=...)`, and
  publishes the public key as a JWKS document referenced by the signature's
  `jku` header.
- Client: verifies the fetched card with `create_signature_verifier` passed
  to `A2ACardResolver.get_agent_card`, resolving keys by `kid` from an
  allowlist of pinned JWKS URLs and an allowlist of algorithms.

The default demo mode runs both, then shows the rejections a verifier exists
for: a card whose transport URL was rewritten in transit, a card with its
signature stripped, and a genuine card whose `jku` the client does not trust.

Adds a smoke test for the sample and a README section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y9eQGQeMVdqZ1f1m8ijGm3
@orangecola
orangecola requested a review from a team as a code owner August 23, 2026 01:56
@github-actions

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

No coverage changes.

Generated by coverage-comment.yml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat]: Add a runnable sample for Agent Card signing and verification

1 participant