Repository navigation
feat(deploy): Run a CodeZip agent in a box on AgentCore Runtime - #2792
mquang-amazon wants to merge 9 commits into
Conversation
agentcore create now writes "box": {} into every new project. The CDK
library then adds the Box gateway (logical name box, AWS_IAM auth).
- Add the optional box field to ProjectSpecSchema, with no default, so
a write-back never adds it to an existing project.
- add gateway-target --gateway box makes the box entry with AWS_IAM in
a Box project. When its last target goes, the entry goes too.
- remove gateway --name box removes only the entry, and says that the
Box gateway stays.
- remove all also deletes the marker, so the next deploy shows only the
existing teardown confirmation.
- A deploy records the Box gateway in deployed-state.json. A later
deploy with no marker but with other resources stops with an error,
unless the user confirms or passes --yes.
- A project with only the marker is a deploy, not a teardown.
…sthrough Box targets
- Replace the "box": {} marker with "projectType": "box". create writes
it, remove all removes it, and no other command adds it.
- add gateway-target --gateway box writes the box entry with
protocolType "None" and refuses any target that is not passthrough,
before it changes agentcore.json.
- add gateway refuses the name box, in any letter case, in a Box
project, and points to add gateway-target --gateway box.
- The deploy check and its messages now name "projectType": "box".
Templates whose agent calls a model now write "projectType": "box" and a "box" agentCoreGateways entry to agentcore.json. The CDK library owns the Box gateway (AWS_IAM, HTTP only) for a Box project; the entry only adds targets to it. The entry gets a us-west-2 "bedrock" passthrough target when the agent calls Bedrock (the Bedrock provider, or LiteLLM on a bedrock/ model). MCP, BMA and empty templates stay as before. The project schema accepts projectType, with no default, so existing projects are written back unchanged. Needs aws/agentcore-l3-cdk-constructs#454 for the CDK library to read projectType and add the Box gateway.
An MCP server calls no model when created, but its tools, or a model added later, still need a gateway for their outbound calls. mcp-python-fastmcp now writes "projectType": "box" and a box entry with no targets: a template with no model provider gets no Bedrock target.
A Box project now has one Box gateway for each runtime (1:1), named <runtime>-box, with _ changed to -. - add gateway-target --gateway <runtime>-box writes the entry of that runtime's gateway. An unknown gateway name in a Box project lists the Box gateways in the error. - add gateway refuses each name that ends in -box, in any letter case, in a Box project. - remove runtime also removes the entry of its Box gateway. - Deploy records all Box gateways of a target (resources.boxGateways) and the deploy check names all of them.
# Conflicts: # src/handlers/project/shortcuts.ts
create wrote a box profile for `agentcore dev`, but a deployed CodeZip agent ran unboxed: nothing shipped box to the Runtime or started it there. create now writes start.py and start.sh into every Python CodeZip template and sets entrypoint to start.py. On build and deploy, the CDK backend stages box from AGENTCORE_BOX_SOURCE (a staging-boxy checkout with an Amazon Linux 2023 build) into the runtime's .box-dist/, so the zip carries it, with the AgentCore Runtime profile rendered from agentcore.json: no reload, signed with the execution role through the default AWS chain, and placeholders that start.sh fills in with the Runtime's paths and the deployed resources' variables. If box fails or the agent does not answer in 22s, start.sh serves main.py without box. When a boxed runtime has its default Memory, build and deploy add the Box gateway's `agentcore` passthrough target and route the agent's Memory calls through it, and write agentcore/box-memory-policy.json, listed in the gateway's new additionalPolicies, so the gateway role may call Memory. The local profile is unchanged byte for byte.
A template whose agent calls Bedrock (the Bedrock provider, or LiteLLM on a bedrock/ model) now writes the extension entry <runtime>-box with a us-west-2 "bedrock" passthrough target, so the agent's model calls go through its Box gateway from the first deploy. The entry has the settings that `agentcore add gateway-target` gives one. Templates with no model provider (MCP server, BMA), other model providers and the empty template write no entry; their runtimes still get a Box gateway from "projectType": "box". Builds on #2787 (projectType and the <runtime>-box entries) and aws/agentcore-l3-cdk-constructs#454 (one Box gateway per runtime).
…y-box-codezip Adapts the CodeZip box deploy to per-runtime Box gateways (<runtime>-box): the Memory target and policy go on each boxed runtime's own gateway, as resolved in box-integration-development.
leo237
left a comment
There was a problem hiding this comment.
Review of the deploy-CodeZip part of this stack (commit 4211fe1 and the merge resolution). I ran the new and changed unit tests locally (boxCodeZip, boxMemory, boxProfile, dev/box, gateway schema): 88 pass. I also checked the L3 CodeZip packager: it does not exclude .box-dist/, and it keeps the exec bit, so the staged box binary ships and runs.
Findings:
- High: the Memory route only works in us-west-2. The
agentcorepassthrough target, the route'sdestinations, and the SigV4 region of the route to the gateway all useBOX_GATEWAY_BEDROCK_REGION. A Memory is a regional resource, and the agent's Memory client callsbedrock-agentcore.<AWS_REGION>. So a deploy to any other region either matches no route or reaches the wrong region's data plane. The policy file's*region suggests every region was meant to work. (inline) - High (design): start.sh fails open. If box fails to start, or the agent in it takes longer than 22s to answer,
main.pyruns without the box. It still holds the full execution role and has unrestricted egress, and the only sign is one[box] FALLBACKlog line. The design doc treats the box as the containment boundary, and the security notes from the 10/28 meeting stress telling customers exactly what the box does and does not give them. Falling back silently to no containment works against that. Please consider failing closed by default and making the fallback opt-in. (inline) - Medium: port race in the timeout fallback. After
kill "$pid"the script does not wait, somain.pycan try to bind the ingress port while box still holds it, and box's own agent child may be left running. (inline) - Medium (design question): Bedrock skips the Box gateway on the Runtime. The Runtime profile signs Bedrock calls straight from the microVM with the execution role. The
bedrocktarget that create puts on<runtime>-boxis then never used on the Runtime, and the execution role needsbedrock:InvokeModel. That conflicts with the design ("egress calls that need credentials leave the microVM"), and it is inconsistent with Memory, which this PR routes through the gateway. If this is a deliberate RIV shortcut, please say so in the code comment. (inline) - Low: start.sh has no tests. Nothing tests the placeholder fill-in, the
# @AGENT_ENV@substitution and the env allowlist regex, or the fallback paths. A small bash-driven test with a fakeboxbinary would cover the logic most likely to regress. A missingAGENTCORE_GATEWAY_BOX_URLalso fails silently today: the Memory backend URL becomes/agentcore. A warning there would help.
One more thing to keep in mind for merge order: every Python CodeZip project that create makes now has the entrypoint start.py. Once this stack reaches main, agentcore deploy refuses to deploy those projects unless AGENTCORE_BOX_SOURCE is set.
— 🤖 Abhilash's Claude Bot
| url = ${str(`${BOX_GATEWAY_URL_PLACEHOLDER}/${target}`)} | ||
| auth.ref = ${str(RUNTIME_AWS_SIGNER)} | ||
| auth.aws.service = "bedrock-agentcore" | ||
| auth.aws.region = ${str(BOX_GATEWAY_BEDROCK_REGION)} |
There was a problem hiding this comment.
High: the Memory route is tied to us-west-2. destinations is bedrock-agentcore.us-west-2.amazonaws.com (via BOX_GATEWAY_AGENTCORE_HOST), and the gateway call is signed for us-west-2. On the Runtime, the agent's Memory client calls bedrock-agentcore.<AWS_REGION>, and both the Memory and the <runtime>-box gateway live in the deploy region. A deploy to us-east-1 (for example) then has no route for the agent's Memory calls, and the SigV4 region is wrong for the gateway. The us-west-2 constant is a stated tradeoff for Bedrock models, but it does not carry over to a regional resource like Memory. Suggestion: have start.sh fill in the region from AWS_REGION with an @REGION@ placeholder, as it does for @GATEWAY_BOX_URL@, and resolve the target endpoint per deploy target. The policy file already uses * for the region.
— 🤖 Abhilash's Claude Bot
| return { | ||
| name: BOX_GATEWAY_AGENTCORE_TARGET_NAME, | ||
| targetType: "passthrough", | ||
| passthrough: { endpoint: `https://${BOX_GATEWAY_AGENTCORE_HOST}`, protocolType: "CUSTOM" }, |
There was a problem hiding this comment.
Same region problem from the gateway side. This passthrough always forwards to bedrock-agentcore.us-west-2, so a Memory deployed to another region is called on the wrong regional data plane, and the gateway role cannot reach it. This needs to follow the deploy target's region. The CDK library could resolve it, or deploy could write it per target.
— 🤖 Abhilash's Claude Bot
|
|
||
| # --- Main -------------------------------------------------------------------------------------------- | ||
| [ "${BOX_START_DISABLE:-}" = 1 ] && serve_without_box "BOX_START_DISABLE=1" | ||
| [ -x "$BOX" ] && [ -f "$DIST/profile.toml" ] || serve_without_box "no $DIST; deploy stages it" |
There was a problem hiding this comment.
High (design): fails open. Every path here, and the 22s timeout and box run exit paths in start_box, ends in exec python main.py with no box. The agent then runs with the full execution role, unrestricted egress and no Dogwood policy, and the only sign is a single [box] FALLBACK log line. A slow cold start, a bad profile, or a missing .box-dist (for example, a deploy without staging) quietly downgrades a contained agent to an uncontained one. Box is the containment boundary in the architecture doc, and the security notes stress telling customers what the box does and does not give them. Suggest failing closed by default: exit non-zero so the Runtime health check fails visibly. Keep the unboxed fallback behind an explicit opt-in such as BOX_START_FALLBACK=1, the counterpart of BOX_START_DISABLE.
— 🤖 Abhilash's Claude Bot
| fi | ||
| sleep 0.5 | ||
| done | ||
| kill "$pid" 2>/dev/null |
There was a problem hiding this comment.
Medium: port race. kill "$pid" sends SIGTERM but does not wait, so serve_without_box execs main.py right away. Box opened the ingress port (0.0.0.0:8080 for HTTP) before the agent was up, and main.py binds that same port. If box has not closed its listener yet, the fallback dies with EADDRINUSE and the Runtime gets nothing. The agent child that box started on the workload port may also outlive box. Suggest kill "$pid"; wait "$pid" 2>/dev/null with a short bounded wait, escalating to kill -KILL if needed. That matters even more if finding 2 keeps any fallback.
— 🤖 Abhilash's Claude Bot
| ? "" | ||
| : onRuntime | ||
| ? ` | ||
| # Sign the model calls with the Runtime's execution role. |
There was a problem hiding this comment.
Design question. On the Runtime, Bedrock is signed inside the microVM with the execution role and does not go through the <runtime>-box gateway's bedrock target, which create still declares. Memory, by contrast, goes through the gateway in this PR. The architecture doc says credentialed egress leaves the microVM for the gateway, and instance credentials are used only to invoke the gateway. As written, the execution role needs bedrock:InvokeModel, and the gateway's bedrock target is unused on the Runtime. If this is a deliberate shortcut for now, please say so in this comment and track the follow-up. Otherwise this route should point at ${GATEWAY_BOX_URL}/bedrock, as the Memory route does.
— 🤖 Abhilash's Claude Bot
Description
agentcore deployruns a Python CodeZip agent in a box on AgentCore Runtime. Before this, onlyagentcore devran it in a box (#2790). Nothing shipped box to the Runtime or started it there. The Strands container template follows in a stacked PR.start.pyandstart.shinto every Python CodeZip template (Strands HTTP/A2A/AG-UI, LangChain, minimal, FastMCP) and setsentrypointtostart.py. The Runtime needs a.pyentry point, sostart.pyhands over tostart.sh.start.shfills in the Runtime profile and runsbox runin/tmp/box/workspace. If box fails or the agent does not answer within 22s, it servesmain.pywithout box (the Runtime allows 30s).BOX_START_DISABLE=1skips box.CdkBackendstages<codeLocation>/.box-dist/before synth, so the zip carries box. The stage includes box, its helpers, its packs, the libffi shim, and the Runtime profile, all fromAGENTCORE_BOX_SOURCE(a staging-boxy checkout'starget-al2023/). If that variable is unset for a boxed runtime, build fails with an actionable error.renderRuntimeBoxProfilerenders the profile from agentcore.json. It has no reload. Bedrock is signed with a bareaws://, the default chain, which resolves to the execution role. Its placeholders (@CODE@,@PYTHON@,@PYTHON_PREFIX@,@GATEWAY_BOX_URL@,# @AGENT_ENV@) are filled in bystart.sh. Only the deployed Memory/Gateway IDs and URLs reach the agent's env. A non-Bedrock provider gets no route yet, because on the Runtime its key comes from AgentCore Identity.agentcorepassthrough target (bedrock-agentcore.us-west-2, signed with the gateway role) to that runtime's own Box gateway,<runtime>-box, recreating the entry if it was removed. Anagentcoretarget on a non-Box gateway is an error. They also writeagentcore/box-memory-policy.jsonand list it in that gateway's newadditionalPolicies. The raw agentcore.json is patched, so unknown fields survive. A second run changes nothing.agentCoreGateways[].additionalPolicies, which cannot be combined withexecutionRoleArn.start.pybut has no.box/profile.tomlis refused, becausestart.pyruns no agent of its own.The local (
agentcore dev) profile is byte-identical to #2788's for every template and model provider.Depends on
a5a5a81b, thenf98580fefor its latest commits) until it lands, so the diff shows its commits for now. Conflicts are resolved as inbox-integration-development(71f93924):DEFAULT_EGRESS_BEDROCK_REGION→BOX_GATEWAY_BEDROCK_REGION, and the Memory target moves from the singleboxgateway to each runtime's<runtime>-boxgatewayadditionalPolicieson the Box gateway role. It is not released or pinned yet<runtime>-box), which this PR now followsAGENTCORE_BOX_SOURCE, until box ships a Linux releaseRelated Issue
N/A (box integration)
Documentation PR
Not applicable.
Type of Change
Testing
bun test. The only failures also fail on an unmodified base on this host:toolAvailable,requireTool,SourceResolver bytes ... under node. Themigratetests timed out once under load and pass when run alonebun run test:e2e: not run. It needsAGENTCORE_BOX_SOURCEand the unreleased CDK librarybun run typecheck,lint:check,format:check,audit,secrets:check,buildsrc/assets/and updatedmanager.test.ts.snap(start.py/start.sh,entrypoint: start.py)command.mdChecklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.