turul-a2a-aws-lambda
AWS Lambda adapter for the turul-a2a server framework — a thin wrapper
over the same axum::Router used in binary deployments.
- Request/response transport (HTTP + JSON-RPC).
- Streaming (SSE) verified end-to-end via
cargo-lambda. - API Gateway / Lambda Function URL authorizer anti-spoofing for trusted identity injection.
- Same-backend enforcement with shared storage (DynamoDB or PostgreSQL) so task state and durable events coordinate across cold starts and concurrent invocations.
The adapter is request/response-shaped: a single Lambda invocation
processes one HTTP request or one SQS batch and returns. Streaming
endpoints are buffered (the entire executor run executes within the
invocation); persistent SSE / :subscribe connections are not
supported on Lambda by design.
Enable the dynamodb feature to propagate DynamoDB support to the
underlying turul-a2a crate:
= { = "0.2", = ["dynamodb"] }
Quick start
use LambdaA2aServerBuilder;
let handler = new
.executor
.storage
.build?;
handler.run.await?;
run() is the universal entry point — it auto-detects whether the
incoming Lambda event is an HTTP request or an SQS batch and dispatches
accordingly. For pure single-event-type Lambdas the runner methods
run_http_only(), run_sqs_only(), and run_http_and_sqs() are
available for adopters who prefer to be explicit.
Push-notification recovery
Push-notification delivery on Lambda requires external triggers.
A Lambda invocation starts no push fan-out: the execution environment
may be frozen indefinitely between invocations, and a storage request
signed before a freeze is rejected as expired after it. The in-process
PushDeliveryWorker reclaim loop the binary server uses has no
equivalent. Correctness is carried by an atomic pending-dispatch
marker written inside the request Lambda's commit transaction, plus
two external workers:
| Worker | Trigger | Role |
|---|---|---|
LambdaStreamRecoveryHandler |
DynamoDB Stream on a2a_push_pending_dispatches (NEW_IMAGE) |
Fast path — fires within ~1s of the marker commit. DynamoDB backends only. |
LambdaScheduledRecoveryHandler |
EventBridge Scheduler (e.g. every 5 min) | Mandatory backstop for all backends. For SQLite/Postgres/in-memory this is the only recovery path. |
Minimal wiring — see examples/lambda-stream-worker and
examples/lambda-scheduled-worker:
// Stream worker (DynamoDB only)
use LambdaStreamRecoveryHandler;
let handler = new;
// Lambda input type: aws_lambda_events::dynamodb::Event
// Response type: aws_lambda_events::streams::DynamoDbEventResponse
// Scheduled worker (all backends)
use ;
let handler = new
.with_config;
// Lambda input type: aws_lambda_events::eventbridge::EventBridgeEvent
// Response: LambdaScheduledRecoveryResponse (per-stage counts + error sample)
// Optional: also cancel tasks left in INPUT_REQUIRED / AUTH_REQUIRED for
// longer than the A2A handler's RuntimeConfig::idle_task_timeout.
let handler = handler.with_idle_task_sweep;
Release-gate invariants (pinned by tests in src/stream_recovery_tests.rs and src/scheduled_recovery_tests.rs):
- Valid INSERT + successful redispatch → no
BatchItemFailure, one POST per config. get_task → Ok(None)(task deleted) → delete marker, return success.- Transient storage error →
BatchItemFailurewith the record'sSequenceNumber. - Unparseable NEW_IMAGE →
BatchItemFailure(logged). - Duplicate stream records → exactly one POST (claim fencing).
- Stream worker and scheduled sweep racing on one marker → exactly one POST.
- Scheduled sweep: stale markers recovered, transient errors counted and markers retained, reclaimable claims redispatched, batch limits honoured with the remainder left for the next tick.
The request Lambda must opt in on both sides, or build() refuses
the configuration: the storage writes the marker only when built with
.with_push_dispatch_enabled(true), and the builder consumes it only
when .push_delivery_store(storage.clone()) is wired.
Operator responsibilities (not framework-managed):
- Provision the DynamoDB Stream (NEW_IMAGE view) on
a2a_push_pending_dispatches— DynamoDB backends only. - Create an event-source mapping with
FunctionResponseTypes: ["ReportBatchItemFailures"], a DLQ, andMaximumRetryAttempts. - Create an EventBridge Scheduler schedule (recommended every 5 min) targeting the scheduled worker.
- Grant IAM: stream-worker reads the stream, scheduled-worker scans + deletes markers, both read tasks and configs.
- Set TTL on
a2a_push_pending_dispatchesto outlast the worst-case scheduler outage.
See also
- Workspace README — project overview, crate map, server/client basics.
examples/lambda-agent,examples/lambda-durable-{agent,worker,single}— runnable Lambda topologies.examples/lambda-stream-worker,examples/lambda-scheduled-worker— push-recovery workers.
License
Licensed under either MIT or Apache 2.0 at your option.