AWS Builder Center
Agents for Humans: Building a Bounded Strands Agent on AWS Lambda

Agents for Humans: Building a Bounded Strands Agent on AWS Lambda

The implementation journey behind a real Strands Agent on Lambda, including scoped credentials, reproducible packaging, fail-closed model integration, and lessons from AWS service constraints.

For the Agents for Humans Hackathon, I wanted to build more than a local agent demo. The project needed a public, testable AWS path while keeping the model inside a strict authority boundary.
The result is AssureOps Sentinel: a Strands-powered evidence sentinel for risky software changes. It runs as a Python 3.12 AWS Lambda, serves a React review workbench and FastAPI API, calls DeepSeek through the Strands OpenAI-compatible adapter, and uses deterministic policy for every release decision.
This article covers how I built and deployed that path, including the decisions that did not work on the first attempt.

Start with the runtime contract

Before choosing AWS services, I wrote down the runtime contract:
  • The model may reason and propose.
  • The application owns obligation identifiers and allowed scope.
  • A reviewer may authorize one exact plan digest.
  • Only a typed executor can run an action.
  • Only verified, exact-revision evidence can satisfy an obligation.
  • Only deterministic policy can return  ready .
That contract prevented the infrastructure from silently expanding the agent's authority. Whether the model runs through DeepSeek or a future Bedrock backend, the surrounding rules remain the same.

The deployed AWS architecture

The verified competition path uses:
  • AWS Lambda for the Python application and compiled React UI;
  • a Lambda Function URL for stable HTTPS access;
  • AWS Secrets Manager for the DeepSeek API key;
  • Amazon S3 for content-addressed ZIP packages;
  • AWS CloudFormation for the function, role, URL, permissions, and log group;
  • Amazon CloudWatch Logs with seven-day retention; and
  • Strands Agents SDK with the OpenAI-compatible model adapter.
The public repository also contains optional Bedrock and AgentCore adapters. They are not represented as live competition infrastructure. The actual tested route is Lambda plus Strands plus DeepSeek.

Keep model credentials outside the package

The deployment package never contains the model key. The Lambda execution role can read one named Secrets Manager secret and write its own logs. It cannot enumerate unrelated secrets or write deployment state.
A simplified version of the CloudFormation policy is:
YAML
Policies:
- PolicyName: ReadOnlyDeepSeekCredential
PolicyDocument:
Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Ref DeepSeekSecretArn
At cold start, the Lambda entrypoint reads the secret, places it into the application composition boundary, and constructs the normal FastAPI service. Provider errors are sanitized before they reach API responses or logs.
The shared judge token follows a different boundary. CloudFormation receives only its SHA-256 digest. The raw token remains in an ignored local file and Devpost's private testing instructions. The frontend reads the token from the URL fragment, stores it in browser session storage, removes it from the address bar, and sends it only as a bearer token to the same-origin API.

Make the model call bounded

The application does not give Strands a shell, arbitrary HTTP tool, database-write tool, approval tool, evidence-write tool, or release tool.
Instead, it sends:
  • a synthetic change description;
  • the exact candidate revision;
  • a source digest;
  • server-derived obligation identifiers;
  • allowed evidence types;
  • one allowed action kind; and
  • a strict Pydantic output schema.
After Strands returns, the application performs dynamic checks that a static JSON schema cannot express by itself. It compares the proposed set of obligations with the required set, validates the target asset and evidence semantics, and verifies that the action covers exactly the declared obligations.
If any check fails, Sentinel uses a deterministic review-only proposal. The fallback is not an attempt to imitate model wording. It preserves the safe operational next step without changing the evidence or release result.

Build a reproducible Lambda package

The Python dependencies include native Linux wheels, while the build workstation is Windows. A normal local  pip install  would create an invalid package.
The package builder therefore:
  1. runs a clean React production build;
  1. installs the fully pinned Linux/x86_64 dependency lock;
  1. places the SPA, Python package, and Lambda entrypoint at ZIP root;
  1. normalizes archive ordering, timestamps, and permissions;
  1. enforces Lambda's expanded-package size limit; and
  1. writes package and dependency-lock SHA-256 values to a manifest.
The verified package was approximately 36.5 MB compressed and 91.1 MB expanded. Two clean builds produced the same package digest. S3 stores the archive under that digest:
text
s3://<private-deployment-bucket>/lambda/<package-sha256>.zip
All four S3 public-access blocks are enabled, and default encryption uses AES-256.

Deploy with CloudFormation, not console memory

The CloudFormation stack declares a 512 MiB, 120-second Python 3.12 function, its least-privilege role, seven-day log retention, the Function URL, and its permissions.
The deployment script verifies the active AWS identity, reapplies S3 access controls, uploads the content-addressed package, creates or updates the model secret without printing it, deploys the stack, and writes the private tokenized judge URL to an ignored file.
The live stack reached  UPDATE_COMPLETE . The active Lambda code digest matched the build manifest.

What did not work—and why I kept it visible

I first prepared Amazon Bedrock and Bedrock AgentCore paths because they are natural homes for this kind of agent. The AWS account had AgentCore Runtime quotas applied as zero, and Bedrock access required additional account allowlisting.
I did not fabricate corporate registration material or claim that a template was a live runtime. Instead, I kept those adapters optional and deployed the primary Strands contract through Lambda using the third-party model route permitted by the hackathon rules.
That decision improved the project. It forced a clean separation between:
  • the Strands agent contract;
  • the model backend;
  • the AWS hosting boundary; and
  • deterministic authority.
Changing the model backend no longer changes the safety model.

Live observations

The deployed runtime returned:
JSON
{
"status": "ok",
"cloud_runtime": "aws_lambda",
"agent_framework": "strands_agents",
"model_id": "deepseek-chat",
"model_authority": "proposal_only"
}
The real Strands proposal completed in approximately 15.6 seconds. Cold initialization used about 125 MiB, and peak memory during the model-backed workflow remained around 194 MiB.
Anonymous access to stateful APIs returns  401 . The private judge flow can reset the synthetic scenario, inspect the proposal, record an independent review, execute one allow-listed suite, verify the evidence, and inspect the audit chain.

Operational hardening after deployment

The demo now has:
  • a monthly AWS Budget with actual and forecast notifications;
  • alarms for Lambda errors, throttles, invocation spikes, and p95 duration near timeout;
  • seven-day log retention;
  • a low account concurrency ceiling; and
  • a periodic external health check that never reads the private token.
The goal is not to pretend a hackathon sandbox is a production platform. It is to make the demonstrated path reproducible, bounded, observable, and honest about its limits.

What I learned

The most useful AWS architecture was not the one with the most services. It was the smallest deployment that preserved the application contract and produced verifiable runtime evidence.
Strands remains responsible for reasoning. Lambda provides a bounded execution and hosting surface. Secrets Manager protects the model credential. S3 and CloudFormation make the package and runtime reproducible. CloudWatch shows what actually happened.
Everything else remains a proposal until there is live proof.

Try the project

Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article