Repository navigation
feat(example): Codex SDK in a Strands Box on a MicroVM, on bedrock-runtime - #594
Merged
Merged
Conversation
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
The image runs agentd as root, as every other example's does, and the trust contract's reason applies unchanged: the control API grants root by design and the MicroVM is the isolation boundary. This example also needs root at start to give its sandbox a private mount namespace with a fresh /proc before vm/launch_in_fresh_proc.py drops to uid 1000. The SBOM and vulnerability scans job failed on PR #594 with DS-0002 (HIGH) for this file alone; the same trivy command run locally exits 0 with the path added.
9 of 10 tasks
…ntime examples/strands-box-codex-sdk runs Codex, driven by the Codex Python SDK, inside a Strands Box inside a Lambda MicroVM. The model is GPT-6.1 Sol through Codex's built-in amazon-bedrock-runtime provider, on bedrock-runtime's Responses endpoint. A Dogwood policy decides each shell command, file operation and outbound connection, and Box enforces each decision outside the Codex process. The SDK starts the contained app-server through CodexConfig.launch_args_override, so its JSON-RPC session passes through `strands-box run`. Box's egress gateway adds the Bedrock API key to requests to bedrock-runtime, and Codex holds a placeholder. vm/driver.py runs six scenarios, one Codex thread each, and checks Box's decision log for the rule each one targets: a temporal rule that permits RELEASE.md only after a passing python3 run, .env reads, host programs, the metadata service, and deletes. Box publishes no Linux release. build-box.sh builds a pinned main commit for aarch64 with cargo-zigbuild against glibc 2.34, and run.sh copies the binaries into each VM. Box's Linux backend mounts a fresh procfs in its user namespace, which the kernel refuses while every procfs in view has a mount over part of it, and a guest always has one. The image is built with --repair-identity, and vm/launch_in_fresh_proc.py gives Box a private mount namespace with a fresh /proc before it drops to uid 1000. docs/PLATFORM.md gains the dated capability and /proc measurements behind that. vm/probe_mmds.py shows the metadata service reachable from uid 1000 outside the box (IMDSv2 token PUT, HTTP 200) and unreachable inside it: a raw connect fails with ENETUNREACH, and the gateway answers 403 citing the metadata_hosts rule. Measured 2026-10-12, us-east-1: run.sh passed 6 of 6 scenarios twice, in 1 min 50 s and 1 min 42 s with the image reused, and terminated each VM.
The probe inside the box is now a check: box/probe-box.toml sets PROBE_WHERE=inside, and vm/probe_mmds.py exits 1 unless the raw connect gets no route and one of policy.dw's metadata rules refuses the token PUT at the egress gateway. strands-box run exits with its agent's status, and run.sh exits non-zero when that probe or the driver fails. The six scenarios alone could not see this: with the metadata rules replaced by a permit for all egress, all six still passed, because Box's shell curl refuses link-local literals before the gateway does. run.sh sets its EXIT trap before the launch and reads the VM id from either envelope, since `microvm run --keep` leaves a VM that failed after creation running and names it in the failure envelope. The trap also deletes the host's copy of the Bedrock API key and the staging directory. Stale results.json and records.jsonl are removed before the copy back, so out/ holds only this run's evidence. The in-box probe's stderr goes to a file in the VM and is shown when the probe fails. driver.py checks the .env secret against every item of the turn, serialized in full, and the final answer. It previously read only the last 400 characters of each command's output. build-box.sh builds with cargo-zigbuild against glibc 2.34 on every host, and exports Box's pinned toolchain as RUSTUP_TOOLCHAIN, because mise inside this repo exports the repo's toolchain, which lacks the aarch64 target the script installs. Measured 2026-10-12, us-east-1: run.sh passed the probe and 6 of 6 scenarios in 99 s. A copy with the metadata rules replaced by `permit` for net:connect and http:request got a token through the gateway, printed "FAIL: MMDS issued a token", and exited 13 while all six scenarios passed. Both VMs reached TERMINATED.
The image runs agentd as root, as every other example's does, and the trust contract's reason applies unchanged: the control API grants root by design and the MicroVM is the isolation boundary. This example also needs root at start to give its sandbox a private mount namespace with a fresh /proc before vm/launch_in_fresh_proc.py drops to uid 1000. The SBOM and vulnerability scans job failed on PR #594 with DS-0002 (HIGH) for this file alone; the same trivy command run locally exits 0 with the path added.
…ice block docs/PLATFORM.md and docs/TRUST.md gave two answers to what a --repair-identity guest can do: the 2026-08-06 entry said ["ALL"] made identity repair succeed, the 2026-09-12 entry said the guest lacked CAP_SYS_ADMIN and CAP_NET_ADMIN even with repair, and the 2026-09-24 entry showed the hostname and boot-id steps (which need CAP_SYS_ADMIN) succeeding. Each section now states the current fact once: - "Root in the guest is not enough": the 2026-10-12 table of both images' bounding sets, with the 2026-08-06 EPERM result kept where it agrees, and the 2026-09-12 claim removed. - The snapshot section merges the 2026-09-23 result and its 2026-09-24 correction into one account of why agentd repairs at the first run hook. - The MMDS section keeps the credential table and adds the 2026-10-12 measurement: on an identity-repair guest, root replaced the host route for 169.254.169.254 with a blackhole over rtnetlink, after which uid 1000 got EINVAL connecting and EPERM changing the route, execs still worked, and other egress still connected. Without identity repair the guest lacks CAP_NET_ADMIN and route changes return EPERM (2026-09-12). - docs/TRUST.md's identity and execution-role passages say the same, and that the block holds only against processes that don't run as root. The example README no longer says no in-guest block works. The DS-0002 exception in .config/trivyignore.yaml states the capabilities agentd needs instead of a chmod claim, which needs no root for files the daemon creates.
laithalsaadoon
force-pushed
the
feature/strands-box-codex-sdk
branch
from
October 12, 2026 18:36
64cb991 to
7ff4973
Compare
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
…ds it Agents read these docs, and a page that gives two answers to one question is a hazard. A measurement that contradicts an entry now replaces it; git history holds the old one. - PLATFORM.md: merge the duplicate WebSocket, runHookPayload, snapshotBuild, base-version, GetMicrovm, tagging, keepalive and suspend sections into one entry each with their dates, and drop the correction-trail sentences. - The suspended window: the 2026-10-12 re-measure shows it ends a VM the idle policy suspended, and an explicit SuspendMicrovm held a VM 21 times past it with resume working (two samples). PLATFORM, SUSPEND-RESUME and the site pages state that. - The PT5M ready-hook timeout no longer reads as a platform cap; the exec PATH/HOME cases are one statement in PROTOCOL; SUSPEND-RESUME's idle attribution and identity text match the current daemon. - The rule changes in the PR template, the issue template, AGENTS.md, CONTRIBUTING.md, the live-suite page, the agents page and the internals index. - Microvms.CorrectionTrail, an error-level Vale rule in prose:check, rejects the phrasing a kept-alongside correction leaves. Seeded fault prose-correction-trail proves it fires. .vale.ini exempts the three passages PR #594 rewrites until it merges. - Two broken heading citations fixed (live.toml, PROTOCOL.md).
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
…nger needs This branch is stacked on #594, whose rewrite of the capability, snapshot and metadata passages removes the three phrases the exemptions covered, so Microvms.CorrectionTrail holds with none.
3 tasks done
…names the trade Review of #594 found that the 2026-09-12 capability result measured a different condition from 2026-10-12: that VM was `run --image <arn> --repair-identity` on an image built without the flag, and `run` applies the flag only when it builds the image (lifecycle.rs passes it to build_request_from alone). So both measurements hold, and PLATFORM.md now says the capabilities come from the image build. - TRUST.md says "an image built with/without" in the identity and MMDS passages, and dates the step successes to 2026-08-06 and 2026-09-24. - posture.py's docstring no longer says repair never grants CAP_NET_ADMIN. - CHANGELOG's 2026-09-12 entry names the `run --image` condition. - The DS-0002 exception says a USER line would remove the capabilities agentd's four jobs need, that granting them back is root in effect, and when to revisit.
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
…ds it Agents read these docs, and a page that gives two answers to one question is a hazard. A measurement that contradicts an entry now replaces it; git history holds the old one. - PLATFORM.md: merge the duplicate WebSocket, runHookPayload, snapshotBuild, base-version, GetMicrovm, tagging, keepalive and suspend sections into one entry each with their dates, and drop the correction-trail sentences. - The suspended window: the 2026-10-12 re-measure shows it ends a VM the idle policy suspended, and an explicit SuspendMicrovm held a VM 21 times past it with resume working (two samples). PLATFORM, SUSPEND-RESUME and the site pages state that. - The PT5M ready-hook timeout no longer reads as a platform cap; the exec PATH/HOME cases are one statement in PROTOCOL; SUSPEND-RESUME's idle attribution and identity text match the current daemon. - The rule changes in the PR template, the issue template, AGENTS.md, CONTRIBUTING.md, the live-suite page, the agents page and the internals index. - Microvms.CorrectionTrail, an error-level Vale rule in prose:check, rejects the phrasing a kept-alongside correction leaves. Seeded fault prose-correction-trail proves it fires. .vale.ini exempts the three passages PR #594 rewrites until it merges. - Two broken heading citations fixed (live.toml, PROTOCOL.md).
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
…nger needs This branch is stacked on #594, whose rewrite of the capability, snapshot and metadata passages removes the three phrases the exemptions covered, so Microvms.CorrectionTrail holds with none.
laithalsaadoon
marked this pull request as ready for review
October 12, 2026 20:58
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
…ds it Agents read these docs, and a page that gives two answers to one question is a hazard. A measurement that contradicts an entry now replaces it; git history holds the old one. - PLATFORM.md: merge the duplicate WebSocket, runHookPayload, snapshotBuild, base-version, GetMicrovm, tagging, keepalive and suspend sections into one entry each with their dates, and drop the correction-trail sentences. - The suspended window: the 2026-10-12 re-measure shows it ends a VM the idle policy suspended, and an explicit SuspendMicrovm held a VM 21 times past it with resume working (two samples). PLATFORM, SUSPEND-RESUME and the site pages state that. - The PT5M ready-hook timeout no longer reads as a platform cap; the exec PATH/HOME cases are one statement in PROTOCOL; SUSPEND-RESUME's idle attribution and identity text match the current daemon. - The rule changes in the PR template, the issue template, AGENTS.md, CONTRIBUTING.md, the live-suite page, the agents page and the internals index. - Microvms.CorrectionTrail, an error-level Vale rule in prose:check, rejects the phrasing a kept-alongside correction leaves. Seeded fault prose-correction-trail proves it fires. .vale.ini exempts the three passages PR #594 rewrites until it merges. - Two broken heading citations fixed (live.toml, PROTOCOL.md).
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
…nger needs This branch is stacked on #594, whose rewrite of the capability, snapshot and metadata passages removes the three phrases the exemptions covered, so Microvms.CorrectionTrail holds with none.
10 of 12 tasks
laithalsaadoon
added a commit
that referenced
this pull request
Oct 12, 2026
#594 merged statements that an exec naming no user runs as root. With AGENTD_DEFAULT_USER set it runs as the image's user, so the trivy exception and TRUST.md's MMDS paragraph say when an exec runs as root: with user 0, or without user on an image that doesn't set the variable.
22 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
examples/strands-box-codex-sdkruns Codex, driven by the Codex Python SDK (openai-codex0.162.1), inside Box fromstrands-agents/box, inside a Lambda MicroVM. The model is GPT-6.1 Sol (global.openai.gpt-6.1-sol) through Codex's built-inamazon-bedrock-runtimeprovider, on thebedrock-runtimeResponses endpoint. The example uses neither Mantle nor a proxy. A Dogwood policy decides each shell command, file operation, and outbound connection Codex makes, and Box enforces each decision outside the Codex process.CodexConfig.launch_args_override, which startsstrands-box run --config box.toml -- app-server --listen stdio://. The SDK needs no other change.bedrock-runtime.us-east-1.amazonaws.com. Codex holds a placeholder.vm/driver.pyruns six scenarios, one Codex thread each. Each scenario passes on Box's decision log, not on the model's answer: a temporal rule that permitsRELEASE.mdonly after a passingpython3run,.envreads, host programs, the metadata service, and deletes. It also checks that the.envsecret appears nowhere in the turn.vm/probe_mmds.pyruns once outside the box, where it reports that uid 1000 gets an IMDSv2 token, and once as a box's agent, where it is a check: it exits 1 unless the raw connect gets no route and one of the policy's metadata rules refuses the token request.run.shexits 0 only when that probe and all six scenarios pass.build-box.shbuilds a pinnedmaincommit (93c4749) for aarch64 against glibc 2.34.run.shbuilds with--repair-identity, andvm/launch_in_fresh_proc.pygives Box a private mount namespace with a fresh/procbefore it drops to uid 1000.docs/PLATFORM.mdanddocs/TRUST.mdnow give one current answer for what a guest can do with and without identity repair. Before, they held three dated entries that disagreed (2026-08-06, 2026-09-12, 2026-09-24). The rewrite also adds a 2026-10-12 measurement: on an identity-repair guest, root can block the metadata service for every process that isn't root.The diff touches no shipped crate, binding, or CLI code: it is one new example directory, platform and trust docs, and one path added to an existing trivy exception.
Evidence
mise run prose:check: 0 errors, 4,106 warnings, no rule over its ceiling;ruffonconformance/lanes/posture.py.mise run checkon the docs: one current answer per platform fact, and a prose rule that holds it #598 head built on it is below.mise run checkandmise run docs:checkpassed on the top of the stack built on it (feat(example): Codex SDK in a Strands Box on a MicroVM, on bedrock-runtime #594, docs: one current answer per platform fact, and a prose rule that holds it #598, and the core: resume refuses an explicitly suspended VM past suspendedDurationSeconds, which the service still resumes #597 fix at c3f7bc1): 1,802 Rust tests, 0 failed; 223 site tests.mise run checkpassed on c27daa2, b9b0dff, 6c18b89, and 64cb991 (1,799 tests, 0 failed each time; prose warnings at 4,106 after 64cb991, below the 4,111 baseline).ci:sbom'svulnstep) run locally exits 0 on 64cb991. The first CI run failed it with DS-0002 on the new Dockerfile. 6c18b89 adds that path to the existing DS-0002 exception in.config/trivyignore.yaml, and the exception's reason now names the capabilities agentd needs, says aUSERline would remove them and granting them back is root in effect, and says when to revisit (3c52e62).shellcheckonrun.shandbuild-box.sh;ruff format --checkandruff checkon the Python; Vale at 0 warnings on the README.examples/strands-box-codex-sdk/run.sh, us-east-1, 2026-10-12: 6 of 6 scenarios passed in each of three runs (1 min 50 s, 1 min 42 s, and 99 s on b9b0dff, which also passed the in-box probe). Every VM reachedTERMINATED.build-box.shrebuilt Box on x86_64 from inside the repo with mise active. The binary matched the first build's SHA-256 (first 16 hex digits compared), aarch64, withGLIBC_2.34as its newest symbol version.release-before-testsfs:writedeny,tests_before_releasetests-then-releaseshell:exec python3permit, thenfs:write RELEASE.mdpermitread-dotenvfs:readdeny,no_envhost-programshell:spawn unamedeny, no permitinstance-metadatacurlrefuses the link-local literal; no permit names169.254deletefs:deletedeny,no_deletesModel traffic in the decision log:
http:request bedrock-runtime.us-east-1.amazonaws.com:443/openai/v1/responsesonly, about 122k tokens per run. Codex's tries atchatgpt.comandapi.github.comwere denied by default and logged.Metadata service probe (
vm/probe_mmds.py, status lines only, no token printed):MMDS block, the run behind the PLATFORM table (2026-10-12, us-east-1, an image built with
--repair-identity, Amazon Linux 2023.12.20260817, kernel 6.1.166 aarch64; status lines only, no token printed):Parity
microvms.pyi,index.d.ts).Guards
The in-box metadata probe is a live check, so no fault can be registered for it mechanically. What we broke, what failed, and what passed after the restore:
metadata_hostsandmetadata_addressesinbox/policy.dwwithpermit (principal, action in [Box::Action::"net:connect", Box::Action::"http:request"], resource);.[inside the box] FAIL: MMDS issued a token, andrun.shexited 13. All six Codex scenarios still passed on that policy, which is why the probe is the gate.[policy: metadata_hosts]refusal and exited 1 on a default-deny refusal, on an issued token, and with no proxy set.microvmwhoserunfails and names a VM,run.shterminated that VM and exited with the launch's status.No repo gate runs these, because the example needs live AWS.
Follow-ups
build-box.shwhen Box ships a Linux release.aws://PROFILEcredential source reads static keys only, so the example mints a Bedrock API key on the host. An upstream source that reads the default chain would remove that step.build-box.shon an aarch64 host is unverified: every build so far ran on x86_64. The script uses the samecargo zigbuild --target aarch64-unknown-linux-gnu.2.34on both.run --image <arn> --repair-identityaccepts the flag and ignores it.Platform claims
docs/PLATFORM.mdentry changed, and it carries a date, a region, and an API version. Laith set the rule on 2026-10-12: a contradicted entry is removed, not kept beside the new one, because agents that read these docs need one current answer. The template's old wording, which keeps both, changes in a separate docs pull request. Every rewritten section carries its measurement's date, region, and API version:/procmounts (us-east-1, API2025-09-09,al2023-1, kernel 6.1.166 aarch64). It keeps the 2026-08-06EPERMresult, which agrees. The 2026-09-12 result now carries its condition: that VM wasrun --image <arn> --repair-identityon an image built without the flag, andrunapplies the flag only when it builds the image (lifecycle.rspasses it tobuild_request_fromalone), so the guest kepta80425fb. Capabilities come from the image build. run --image ignores --repair-identity #602 tracks refusing the combination.EPERM(2026-09-12). With it, root replaced the host route for169.254.169.254with a blackhole, and uid 1000 then gotEINVALconnecting andEPERMchanging the route (2026-10-12, us-east-1, API2025-09-09, one VM).docs/TRUST.md's identity and execution-role passages say "an image built with/without", and date the step successes to 2026-08-06 and 2026-09-24.conformance/lanes/posture.py's docstring and the 2026-09-12CHANGELOG.mdentry name the same condition.Scope
docs/STRATEGY.md). It is an example that runs one agent in one VM.