Conversation
…tials Adds an agentcore_gateway model backend alongside bedrock and litellm. It reaches the same goal as llm-edge — no upstream model credential ever lands in a container whose user is root — with one fewer service to run: the provider credential lives in the gateway's token vault, so there is nothing on the platform side to hold either. A session's credential is an STS session tagged with its runtime session id, minted by the backend and delivered in the existing llm_credentials block: - a session policy narrows it to InvokeGateway on the one gateway the backend points at, so a credential read out of kernel memory cannot reach any other gateway in the account; - the session tag is what revoke() conditions a Deny on, which is how one session is stopped without disturbing another. The Deny set is rebuilt from a LLMREVOKE partition and pruned once entries outlive the credential they revoke, keeping the inline policy inside its 10 KB limit; - STS cannot extend a session, so rotate() re-mints from the routing already recorded on the token item, preserving the "config edits apply at next warmup" rule the edge path already has. Claude Code cannot sign SigV4, which is exactly why the signing belongs in the kernel shim rather than the CLI subprocess: both shims now sign per request (botocore in the SDK kernel, ~40 lines of node:crypto in the contract server, which keeps its single dependency). Only a minimal header set is signed and the caller's headers are attached afterwards, so Claude Code's anthropic-* and x-stainless-* headers cannot invalidate a signature; its own x-amz-* headers are dropped rather than folded in. The SigV4 path buffers the request body because it needs the payload hash, and is capped; the edge path still streams it through unread. What this backend deliberately does not claim: IAM conditions cannot see a request body, so the per-session model allowlist is not expressible as an IAM condition. The shim's check is a fail-fast optimisation and says so — the session's user is root in that microVM and can bypass it. Real enforcement needs a gateway request interceptor or one gateway per model; the allowlist is recorded on the token item either way so the decision stays auditable. scripts/e2e_agentcore_gateway.py provisions a live gateway and drives the real Claude Code CLI through the real shim, asserting the chain end to end: a multi-turn tool-using task completes over three model calls, the credential is refused on a second gateway, and revoking one session leaves another working. Terraform for the caller role is not included yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Folds the third model backend into the existing model-access sections rather than adding a new page: architecture.md's backend table, security-explainer §9 (which gains the second call chain), deployment.md §2 as Option A2, permissions.md's principal table, the user guide's Governance card, and the EXTENDING.md configuration index. Three things the evaluation turned up that operators need in writing: - The kernel roles already hold InvokeGateway on gateway/* so they can reach MCP tool gateways, and that wildcard covers an inference gateway too — a root user in the microVM could call it on the role's own identity, with no session tag to revoke. Enabling this backend requires an explicit Deny for the inference gateway's ARN; without it the per-session credential is decorative. Documented as a prerequisite in deployment.md and permissions.md. - A gateway interceptor's escaping exception is relayed to the caller with its full stack trace, regardless of exceptionLevel — and the caller is the tenant's microVM. Interceptors must catch everything themselves. The reassuring half: interceptor failure is fail-closed. - allowedRequestHeaders is not optional. Left unset the gateway relays the caller's own x-amz-security-token; once set, a connector target stops relaying content-type unless it is listed. Also corrects two claims §9.4 and §9.5 had been overstating, independent of this backend: the platform's quota counts invocations rather than tokens, so there is no per-user token ceiling on either gateway mode; and credential reuse across sessions is not prevented by either, because nothing proves a caller is the session it claims to be. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
A third model backend,
agentcore_gateway, alongsidebedrockandlitellm. It reaches the same goal asllm-edge— no upstream model credential ever lands in a container whose user is root — with one fewer service to run: the provider credential lives in the AgentCore Gateway's token vault, so there is nothing on the platform side to hold either.Two commits: code, then docs.
How a session is authorized
The credential a kernel receives is an STS session tagged with its runtime session id:
InvokeGatewayon the one gateway the backend points at, so a credential read out of kernel memory cannot reach any other gateway in the account;revoke()conditions a Deny on — that is how one session is stopped without disturbing another. The Deny set is rebuilt from anLLMREVOKEpartition and pruned once entries outlive the credential they revoke, keeping the inline policy inside its 10 KB limit;rotate()re-mints from the routing already recorded on the token item, preserving the "config edits apply at next warmup" rule the edge path has.Claude Code cannot sign SigV4, which is exactly why the signing belongs in the kernel shim rather than the CLI subprocess. Both shims now sign per request — botocore in the SDK kernel, ~40 lines of
node:cryptoin the contract server so it keeps its single dependency. Only a minimal header set is signed and the caller's headers are attached afterwards, so Claude Code'santhropic-*andx-stainless-*headers cannot invalidate a signature; its ownx-amz-*headers are dropped rather than folded in.What this backend deliberately does not claim
IAM conditions cannot see a request body, so the per-session model allowlist is not expressible as an IAM condition. The shim's check is a fail-fast optimisation and says so in the code — the session's user is root in that microVM and can bypass the shim. Real enforcement needs a gateway request interceptor or one gateway per model. The allowlist is recorded on the token item either way so the decision stays auditable.
Verification
scripts/e2e_agentcore_gateway.pyprovisions a live gateway, interceptor Lambda and caller role, then drives the real Claude Code CLI through the real kernel shim and tears everything down. Nothing in the chain is stubbed; the only simulation is that the shim runs in the test process rather than inside AgentCore Runtime, which is the same place it runs in production.It asserts, and currently passes:
tool_resultblocks accumulating 0→1→2, request body growing 112 KB → 114 KB, all streamingrevoke(); session B keeps workingClaude Code sent exactly one path throughout:
POST /v1/messages?beta=true. It never calls/v1/messages/count_tokens, which AgentCore Gateway does not accept inbound — so that gap is not a blocker.Things operators need to know (all documented)
InvokeGatewayongateway/*so they can reach MCP tool gateways, and that wildcard covers an inference gateway too. A root user in the microVM could call it on the role's own identity, with no session tag to revoke. Enabling this backend requires an explicit Deny for the inference gateway's ARN — without it the per-session credential is decorative. Written up as a prerequisite indeployment.mdandpermissions.md.exceptionLevel— and the caller is the tenant's microVM. Interceptors must catch everything themselves. The reassuring half: interceptor failure is fail-closed (verified).allowedRequestHeadersis not optional. Left unset the gateway relays the caller's ownx-amz-security-token; once set, a connector target stops relayingcontent-typeunless it is listed.The docs commit also corrects two claims
security-explainer.zh.md§9.4/§9.5 had been overstating, independent of this backend: the platform's quota counts invocations rather than tokens (so there is no per-user token ceiling on either gateway mode), and credential reuse across sessions is not prevented by either mode, because nothing proves a caller is the session it claims to be.Not included
MaxSessionDuration, the gateway and the kernel-role Deny are described in the docs but not yet a module. Deliberate: I could not exercise a Terraform path end to end here.agentcore_gatewayships disabled, and with the caller role ARN unset the backend refuses to route rather than falling back to a shared credential.Note on stacking
This branch is rebased directly onto
mainand is independent of #39, which touchesllm_credentials_service.pytoo. Whichever merges second will want a look at the ownershipConditionExpressioninmint()/mint_agentcore()— the two express the same rule in their own paths.🤖 Generated with Claude Code