Agent Governance Foundation

How to Add Authorization Gates to AWS Lambda Agent Functions

Agent Governance Foundation
aws-lambdaauthorizationpythonintegration

An agent running inside AWS Lambda doesn't look like an agent framework at all from the outside — it's a handler function, def handler(event, context), invoked by whatever triggered it. But if that handler calls an LLM and lets it decide to write to a database or call an internal API, it needs the same authorization boundary any other agent does. The good news: there's no new adapter to learn. The thing that needs care is warm containers, not the wrapper itself.

Why you need this

Lambda's execution model means a single container can serve many invocations back-to-back before AWS recycles it. Anything held in module-level state — including an AGF client — persists across those invocations. That's normally a performance win. It's also exactly the kind of shared, reused state that a naive integration can get wrong.

Prerequisites

pip install agf-sdk
export AGF_API_KEY=agfk_your_key_here

Guard your handler

There's no agf.aws_lambda module — Lambda's handler contract (a plain callable taking event, context) is close enough to an MCP tool invocation that agf.mcp.guard_tool applies unmodified:

import os
from agf.client import AGFClient
from agf.keys import build_self_signed_chain
from agf.mcp import guard_tool

# Built once, at cold-start, at module scope — reused across warm invocations.
client = AGFClient(api_key=os.environ["AGF_API_KEY"])
AGF_AGENT_ID = "did:agf:agt_01abc"


def _chain_provider(*args, **kwargs):
    # Called fresh on every invocation — a chain built once expires in 5 minutes,
    # and a warm container can easily outlive that between invocations.
    return build_self_signed_chain(AGF_PRIVATE_KEY_PEM, AGF_AGENT_ID, "lambda:process_order", "agf")


@guard_tool(
    client,
    agent_id=AGF_AGENT_ID,
    action_type="lambda:process_order",
    chain_provider=_chain_provider,
    validate_execution=True,
    report_outcome=True,
)
def handler(event, context):
    return process_order(event["order_id"])

Warm containers need agf-sdk >= 0.7.0

This is the one real caveat specific to long-lived clients, and it's a hard version floor, not a style preference: agf-sdk versions before 0.7.0 had a broken sync/async bridge — the internal helper that lets a sync guard_tool() call run an async policy check created a brand-new event loop on every sync-bridged call. A shared AGFClient's pooled HTTP connections bind to whichever event loop first touched them, so the second call on a fresh loop crashed with RuntimeError: Event loop is closed — exactly the shape a warm Lambda container hits on its second invocation. 0.7.0 fixed the bridge to reuse one persistent background loop instead. This is unrelated to delegation-chain expiry; a chain_provider callable doesn't sidestep it — pin agf-sdk >= 0.7.0 regardless of whether you use one.

If your handler already has other decorators

AWS Lambda Powertools (@logger.inject_lambda_context, @tracer.capture_lambda_handler, @metrics.log_metrics) is a common companion in production handlers. Put guard_tool as the innermost decorator, closest to the function — it needs to see the actual event/context arguments, not whatever a tracing wrapper's *args might have reshaped them into:

@tracer.capture_lambda_handler
@metrics.log_metrics
@guard_tool(client, agent_id=AGF_AGENT_ID, action_type="lambda:process_order", chain_provider=_chain_provider)
def handler(event, context):
    return process_order(event["order_id"])

Next steps


The AGF Python SDK is open source. GitHub →