Skip to main content

Overview

The A2AVerificationInterceptor is the central component of QWED A2A. Every inter-agent message passes through it before reaching the recipient.
The trace_id parameter is required and must be provided by the caller. This ensures all verdicts and JWT attestations are deterministic and auditable. The HTTP gateway (POST /a2a/intercept) generates this automatically.

Verification pipeline

1

Schema Validation

The incoming AgentMessage is validated by Pydantic:
  • sender_agent_id and receiver_agent_id must be 1–256 chars, no control characters
  • payload is required (dict)
  • payload_type defaults to GENERAL if not specified
  • Timestamps are timezone-aware UTC
2

Trust Boundary

The trust boundary evaluates the sender→receiver pair:
  • Global blocklist check
  • Pair-level block check
  • Allowlist check (in strict mode)
  • Token-bucket rate limiting
Any agent IDs in config.trusted_agents are pre-added to the trust boundary allowlist at interceptor construction time. There is no separate bypass step — trusted agents still flow through engine routing and receive a normal verdict.
3

Engine Routing

Based on payload_type, the message is routed to the appropriate verification engine (finance_guard, logic_guard, code_guard). Payload types with no engine (GENERAL, DATA_QUERY) return an unverifiable engine result instead of being silently forwarded.
4

Verdict & Attestation

The engine result is wrapped in a VerificationVerdict. FORWARDED, BLOCKED, and HEURISTIC_PASS verdicts are signed with an ES256 JWT attestation. UNVERIFIABLE verdicts carry no JWT — issuing a signed token for content that was never verified would be a false cryptographic claim.

Verification engines

Finance guard

Verifies financial claims using deterministic Decimal arithmetic. Recomputes totals from line items and compares against the claimed_total.
All financial comparisons use decimal.Decimal with ROUND_HALF_UP quantization to 0.01. Floating-point arithmetic is never used in the verification path.
Empty or malformed payloads return UNVERIFIABLE, not FORWARDED. If a FINANCIAL_TRANSACTION payload is missing data, line_items, or claimed_total, or contains non-numeric amounts, the finance guard returns status=unverifiable with no JWT attestation. Earlier releases collapsed these cases to a signed FORWARDED verdict, which falsely attested that the math had been checked.

Logic guard

Detects logical contradictions — claims where the same proposition is both asserted and negated.
Contradictions are sorted before output, ensuring deterministic results regardless of Python’s hash randomization (PYTHONHASHSEED).
Empty or malformed logic payloads return UNVERIFIABLE. A LOGIC_ASSERTION payload with a missing, non-list, or empty assertions array — or with malformed assertion entries — returns status=unverifiable and no JWT attestation. There is nothing to check, so nothing is attested.

Code guard

Scans code payloads using a two-layer approach. AST structural analysis runs first; regex heuristics only run when the AST layer finds nothing. Layer 1 — AST structural analysis (primary). The code is parsed into an abstract syntax tree and inspected for direct dangerous constructs: Layer 2 — Regex heuristic scan (secondary). Catches obfuscation patterns that survive AST parsing: If either layer finds a threat, the verdict is BLOCKED (the reason notes which layer triggered). If both layers are clean, the verdict is HEURISTIC_PASS — a signed attestation that no known dangerous constructs were found, not a deterministic guarantee that the code is safe to execute.

Passthrough (unverifiable)

Messages with payload_type of GENERAL or DATA_QUERY have no verification engine. The interceptor returns an UNVERIFIABLE verdict with engine="passthrough", no JWT attestation, and reason "No verification engine available for this payload type". The message is not silently forwarded — callers must decide whether to route unverified content downstream based on their own policy.

Verdict statuses

Every call to intercept() returns a VerificationVerdict with one of five statuses. Only statuses backed by a real verification decision carry a signed JWT attestation.
UNVERIFIABLE verdicts have attestation_jwt=None. If your downstream code assumes every verdict has a JWT, guard for this — issuing an attestation for content that was never verified would be a false cryptographic claim.

Configuration reference

The InterceptorConfig controls which engines are active:

Error handling

When block_on_error=True (default), any exception in a verification engine results in a BLOCKED verdict. When False, the message is FORWARDED despite the error — useful for observability-only deployments.
Engine exception → BLOCKED verdict with error reason. Safe default for production.
Engine exception → FORWARDED verdict. The error is logged but doesn’t block communication. Use for shadow deployments.