Skip to content

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

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

docs(samples): add Agent Card signing sample#1198
orangecola wants to merge 4 commits 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

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

No coverage changes.

Generated by coverage-comment.yml

@mykytanetipa mykytanetipa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presubmits need to be green before review can be granted.

Only failure is Check Spelling: it flags SECP at samples/agent_card_signing.py. Since check-spelling splits on digits, add a bare SECP to .github/actions/spelling/allow.txt (the existing SECP256R1 won't match).

orangecola and others added 3 commits August 31, 2026 13:43
Review fixes on the Agent Card signing sample:

- Raise instead of printing `ERROR:` when a forged card is accepted. The demo
  previously exited 0 in exactly the case it exists to catch, and the smoke
  test's only guard was a substring check that any stray ERROR log would trip.
- Drop that substring assertion from the smoke test; a non-zero exit now
  covers it.
- Quiet INFO logging in the client modes, where httpx and the card resolver
  (which logs the whole card) buried the numbered walkthrough. `serve` keeps
  INFO, since there the server log is the point.
- Rename `JwksKeyProvider.fetch` to `from_urls`, matching the `PyJWKSet`
  constructors it wraps.
- Check the response status on the JWKS fetch in the last demo case.
- Fix a stale docstring and a docstring wrapped past 80 columns.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y9eQGQeMVdqZ1f1m8ijGm3
check-spelling splits tokens on digits, so `SECP256R1` in the source is
checked as `SECP`, which the existing `SECP256R1` allowlist entry does not
cover. Add the bare token.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y9eQGQeMVdqZ1f1m8ijGm3
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

3 participants