mech.app

The mech.app newsletter

Agentic AI, minus the noise.

Get practical field notes on AI agents, automation, developer tools and security delivered to your inbox.

No spam. Unsubscribe anytime.

AI Agents

Serverless A2A Gateway: Path-Based Routing for Agent Discovery and Access Control

How AWS built agent-to-agent routing with API Gateway, Lambda, and DynamoDB using path-based patterns instead of service meshes or custom protocols.

Source: aws.amazon.com
Serverless A2A Gateway: Path-Based Routing for Agent Discovery and Access Control

When you deploy 20 agents, you face up to 190 point-to-point connections. Each agent needs credentials for every other agent it calls, separate routing logic, and custom access control. The operational burden scales quadratically.

AWS published a serverless A2A gateway pattern on July 1, 2026 that solves this with path-based routing. All agents sit behind a single domain. Standard A2A clients work without modification. The gateway handles discovery, routing, and access control using API Gateway, Lambda, and DynamoDB.

This is not a service mesh. It is not a custom protocol. It is HTTP routing with JWT scopes and a registry.

Why Path-Based Routing Matters

The pattern uses /agents/{agentId} instead of subdomains or port-based addressing. This choice has infrastructure consequences:

  • Single TLS certificate covers all agents. No wildcard DNS or per-agent cert rotation.
  • One API Gateway endpoint handles all traffic. No load balancer sprawl.
  • Standard HTTP clients work unchanged. No custom SDKs or protocol negotiation.

Path-based routing means the gateway extracts {agentId} from the URL, looks up the backend in DynamoDB, and proxies the request. The agent does not need to know it is behind a gateway. The client does not need to know which backend hosts the agent.

Three-Layer Architecture

The gateway splits into management, control, and execution layers.

Management Layer: Agent Registry

A DynamoDB table stores agent metadata:

  • Agent ID (primary key)
  • Backend endpoint (ECS task, Lambda URL, Bedrock runtime)
  • Semantic tags for discovery
  • OAuth credentials for backend authentication

Agents register themselves at startup or via an admin API. The registry supports semantic search so agents can discover peers by capability instead of hardcoded IDs.

Control Layer: JWT Scopes and Lambda Authorizer

API Gateway uses a Lambda authorizer to validate JWTs. The token includes scopes like agent:read:agentId or agent:invoke:*. The authorizer checks:

  1. Token signature (Cognito or external IdP)
  2. Scope match against the requested agent ID
  3. Rate limits or IP allowlists (optional)

If the token lacks the required scope, the request fails before reaching the backend. This centralizes access control. You do not configure permissions on each agent.

Execution Layer: Proxy and SSE Streaming

API Gateway forwards the request to the backend endpoint stored in DynamoDB. The gateway:

  • Adds OAuth tokens for backend authentication (retrieved from Secrets Manager)
  • Preserves headers like Content-Type and X-Request-ID
  • Streams Server-Sent Events (SSE) responses back to the client without buffering

Lambda handles the routing logic. It queries DynamoDB for the backend URL, fetches OAuth credentials, and returns a proxy configuration to API Gateway.

Code Snippet: Lambda Authorizer Logic

import json
import jwt
from jwt import PyJWKClient

def lambda_handler(event, context):
    token = event['authorizationToken'].replace('Bearer ', '')
    
    # Validate JWT signature
    jwks_client = PyJWKClient(JWKS_URL)
    signing_key = jwks_client.get_signing_key_from_jwt(token)
    decoded = jwt.decode(token, signing_key.key, algorithms=['RS256'])
    
    # Extract requested agent ID from path
    agent_id = event['methodArn'].split('/agents/')[1].split('/')[0]
    
    # Check scope
    scopes = decoded.get('scope', '').split()
    required_scope = f'agent:invoke:{agent_id}'
    
    if required_scope in scopes or 'agent:invoke:*' in scopes:
        return generate_policy('Allow', event['methodArn'])
    else:
        return generate_policy('Deny', event['methodArn'])

def generate_policy(effect, resource):
    return {
        'principalId': 'user',
        'policyDocument': {
            'Version': '2012-10-17',
            'Statement': [{
                'Action': 'execute-api:Invoke',
                'Effect': effect,
                'Resource': resource
            }]
        }
    }

This authorizer runs on every request. It adds 10-50ms of latency but eliminates per-agent auth configuration.

Comparison: Gateway vs. Service Mesh vs. Direct Connections

ApproachDNS ComplexityTLS OverheadAccess ControlLatency PenaltyOperational Burden
Direct connectionsHigh (N² entries)High (N certs)Per-agent configNoneQuadratic scaling
Service meshMedium (sidecar injection)Medium (mTLS per hop)Policy CRDs5-15ms per hopKubernetes dependency
Serverless gatewayLow (single domain)Low (one cert)Centralized JWT scopes10-50ms (authorizer)DynamoDB + Lambda

The gateway trades latency for operational simplicity. If you need sub-10ms routing, a service mesh is faster. If you want to avoid Kubernetes and sidecar injection, the serverless pattern works.

Failure Modes and Observability

The gateway introduces new failure points:

  • DynamoDB throttling: If agent lookups exceed provisioned capacity, requests fail. Use on-demand billing or provision read capacity based on request rate.
  • Lambda cold starts: The authorizer and routing Lambda can add 500ms on the first request. Keep functions warm with scheduled invocations or provisioned concurrency.
  • Secrets Manager rate limits: Backend OAuth tokens are fetched per request. Cache tokens in Lambda memory with TTL-based refresh.

For observability, the gateway emits CloudWatch metrics:

  • AuthorizerLatency: Time spent validating JWTs
  • RoutingLatency: Time spent looking up backends in DynamoDB
  • BackendErrors: 5xx responses from agent backends

Enable API Gateway access logs to capture full request traces. Use X-Ray for distributed tracing across the gateway and agent backends.

When Standard A2A Clients Work Without Modification

The gateway is compatible with A2A clients that:

  • Send Authorization: Bearer <token> headers
  • Accept HTTP 307 redirects (optional, for backend failover)
  • Handle SSE streams with Content-Type: text/event-stream

Clients do not need custom SDKs. They do not need to know the backend topology. They call https://gateway.example.com/agents/order-agent/invoke and the gateway handles the rest.

This works because the gateway does not change the A2A protocol. It routes HTTP requests based on path and enforces access control with standard JWT scopes.

Deployment Shape

The full stack includes:

  • API Gateway: HTTP API with JWT authorizer
  • Lambda functions: Authorizer (Python 3.12) and routing proxy (Python 3.12)
  • DynamoDB table: Agent registry with on-demand billing
  • Secrets Manager: OAuth tokens for backend authentication
  • Cognito user pool: JWT issuer for client authentication (optional, can use external IdP)

Deploy with CloudFormation or Terraform. The gateway is stateless. Scale by increasing Lambda concurrency and DynamoDB capacity.

Technical Verdict

Use this pattern when:

  • You have 10+ agents and want to avoid point-to-point connections
  • You need centralized access control without modifying agent code
  • You want to support multiple agent runtimes (ECS, Lambda, Bedrock, external APIs)
  • You can tolerate 10-50ms of gateway latency

Avoid this pattern when:

  • You need sub-10ms routing (use a service mesh or direct connections)
  • Your agents already run in Kubernetes (Istio or Linkerd may be simpler)
  • You have fewer than 5 agents (the operational overhead is not worth it)

The serverless A2A gateway is plumbing for multi-agent systems. It does not orchestrate workflows or manage state. It routes requests and enforces permissions. If you need orchestration, layer it on top with Step Functions or a custom coordinator.