Skip to main content

Overview

Every verification verdict is signed with an ES256 JWT attestation — a cryptographic proof that the verification took place. This enables:
  • Tamper detection — any modification invalidates the signature
  • Non-repudiation — the signing service is identified by DID
  • Audit compliance — attestations are stored and queryable
  • Cross-service verification — any QWED node can verify the token

How it works


JWT structure

The kid is derived from a SHA-256 fingerprint of the public key, so it stays stable as long as QWED_A2A_SIGNING_KEY_PEM is unchanged — even across process restarts.

Payload


Configure the signing key

QWED A2A requires a persistent ECDSA P-256 signing key so that JWT attestations signed before a restart can still be verified afterwards. Keys are never generated inside the process — the service loads them from the QWED_A2A_SIGNING_KEY_PEM environment variable (or the pem_key constructor argument).
If QWED_A2A_SIGNING_KEY_PEM is not set, A2ACryptoService fails closed: calls to sign_verdict, verify_attestation, get_public_key_jwk, and the interceptor’s intercept() raise RuntimeError. This is deliberate — signing with a per-process ephemeral key would silently break audit continuity.
Generate an unencrypted PKCS#8 P-256 key with openssl:
Store the PEM in your secrets manager (AWS Secrets Manager, Vault, etc.) and inject it as an environment variable at runtime. Every replica in the same logical deployment must load the same PEM so all instances share one key_id and can verify each other’s tokens.

Signing a verdict


Verifying an attestation

Verification outcomes


Tamper detection

The sub claim contains a SHA-256 hash of the original payload:
If the original payload is modified after signing, the hash will not match the sub claim — even though the JWT signature itself is still valid. Always verify both the JWT signature and the payload hash.

Publishing the public key (JWKS)

External consumers verify attestations by fetching the public key. A2ACryptoService.get_public_key_jwk() returns the current key in JWK format:
The FastAPI gateway exposes this at GET /.well-known/jwks.json so downstream services and auditors can fetch keys over HTTP.

Cross-service verification

Because every replica loads the same PEM from QWED_A2A_SIGNING_KEY_PEM, they all derive the same key_id and can verify each other’s tokens:
If you rotate QWED_A2A_SIGNING_KEY_PEM, attestations signed with the previous key become unverifiable once the new JWKS is served — the endpoint only publishes the current key. To avoid rejecting in-flight attestations during rotation, keep the previous key accessible until all tokens signed with it expire (check their exp claim). Future releases may add multi-key JWKS support for seamless rotation.

Fail-closed behavior

QWED A2A treats missing or invalid signing keys as an unrecoverable configuration error, not a soft warning. There is no ephemeral-key fallback. The FastAPI gateway surfaces these as HTTP 503 Signing key unavailable on both /a2a/intercept and /.well-known/jwks.json so orchestrators can detect a misconfigured deployment before it accepts traffic.