← All articles

AWS · AgentCore · AI Agents · Security · IAM

Identity and Attribution in a Software Factory for AI Coding Agents on AWS

·23 min read

An AI agent that writes code is easy to try: a laptop is enough. To industrialize that work is a different matter, with many agents that work in parallel on real repositories and deploy to AWS. The final goal of a software factory is full autonomy: agents work and release without a person who checks every step.

Companies hesitate because of one specific fear: that something nobody verified reaches production, and that nobody can say later who did it and why. Autonomy needs trust, and trust comes from guarantees.

One category of guarantees is about identity. An agent acts on AWS with permissions, so the agent and also each work session need an identity that they cannot choose themselves. That identity must then appear in every action, because after a wrong deployment somebody will need to know which agent acted and on which task.

These guarantees must be part of the design of the factory: if all agents use the same IAM role, CloudTrail shows only that role, for every action. The architecture I propose in this article is an attempt to meet these requirements. The architecture starts from the process that we want in a software factory, and gives an answer to each requirement of that process.

Requirements of a Software Factory for AI Coding Agents

The process that we want is simple to describe. Each step, however, has a specific requirement.

A person asks for a change. We want a person to open a task in the repository, in natural language, as with a colleague. The point of attention is that the text of the task becomes the input of an agent, and nobody checked that text. The text of the task must therefore not decide anything about the agent or its permissions.

An agent takes the task and works alone. We want many agents, and many sessions of the same agent, at work at the same time. Here the requirement has two parts: each agent and each session need an identity that they cannot choose, and a session must not be able to change the work of a different session.

The agent runs code for its work. The agent must be able to run the tests and the builds of the repository. That code comes from input that nobody reviewed, so the code must run in a place without credentials and without a path to the network. Without that path, the code cannot exfiltrate data.

The agent acts on AWS and on the repository. We want the agent to deploy a test version and to propose the change with a pull request. The requirement is that every action names the agent and the session that performed the action, and that the agent never has credentials that it could pass to somebody else.

The change reaches production without a person who checks it. That is the final goal. To reach that goal, the content that reaches production must be trustworthy. The requirement is therefore that the agent cannot promote its own change, and that the step to the live environment depends on checks outside the session of the agent.

Later, an audit must be able to rebuild the full history. Starting from a change in the live environment, an audit must find the commit, the session, the agent and the task of that change, in the AWS logs and also in the repository.

AI Coding Agent Workflow from GitHub to AWS

In the architecture, the process works like this. A person opens a GitHub issue in the repository and adds a label. An agent session starts, changes the code and deploys a test version to a stack of the session. At the end, the session opens a pull request. The merge of the pull request then brings the change to the live environment.

The text of the issue decides nothing. The repository of the request determines the use case, and a small function, the session starter, decides which agent works on the issue and starts the session. The agent receives the text only as a description of the work.

The agent stops at the pull request because the agent has no permission to merge and no permission on the live stack. A separate workflow does the live deployment with its own role, and that workflow starts only after the merge.

The choice of the merge step belongs to the operators of the factory. At first, a person can merge after a review: while the system has no trust yet, somebody reviews every change. Later, automated checks on the pull request cover the same step, and the merge happens without a person. The architecture stays the same, because the architecture only requires that the agent is not the component that promotes the change.

The flow of a task, from GitHub to the live environment
The flow of a task, from GitHub to the live environment

Missing Session Identity in AWS CloudTrail Events

CloudTrail records the IAM principal of every request. For a role, the record has the role and the name of the role session. If ten agents share one role, the ten agents look the same in the logs.

A managed runtime does not solve the problem alone. Amazon Bedrock AgentCore Runtime is the AgentCore service that runs agents: it gives every session of an agent a separate microVM, a lightweight virtual machine for that session only, isolated from the other sessions. The agent process runs in the microVM, with the credentials of the runtime role. The role session of those credentials has the name of the microVM and no source identity, so CloudTrail shows the agent runtime but not the session.

The architecture therefore needs two things: an identity that the session cannot choose, and credentials that contain that identity for every AWS action.

The actions that each part of the system can perform
The actions that each part of the system can perform

Session Identity with AgentCore Runtime and AWS STS

An agent must not be able to declare its own identity. An ID in the arguments of a request is text that the model writes, and a hostile task can easily make the model change that text. The identity of a session must therefore be a signed token that the agent can neither create nor change.

AWS STS can sign that token. With the GetWebIdentityToken action, STS signs a JSON Web Token (JWT) for an IAM principal. The request can include tags, and STS writes the tags into the token as claims.

STS signs a token for a role, but all sessions of an agent use the same runtime role. To distinguish one session from another, IAM needs a value that it can read, and version V2 of AgentCore Runtime gives IAM that value: the credentials of a session contain the runtime session ID as an IAM context key, aws:AgentCoreRuntimeSourceSid, and a policy can compare a value of a request with that key.

With STS and the context key together, a session can request its own token. The permission for that request goes in the IAM policy of the runtime role, with a condition: IAM permits the request only if the session ID in the tags equals the ID of the runtime session that sends the request. If a session tries to use the ID of a different session, IAM denies the request.

This is the statement in the runtime role of agent-alpha, as CloudFormation deploys it. The same statement also fixes the agent of the runtime and the repositories of that agent. A second statement permits sts:GetWebIdentityToken itself, only for the audience of the gateway and with one signature algorithm.

{
  "Effect": "Allow",
  "Action": "sts:TagGetWebIdentityToken",
  "Resource": "*",
  "Condition": {
    "ForAllValues:StringEquals": {
      "aws:TagKeys": ["agent_id", "session_id", "repository"]
    },
    "StringEquals": {
      "aws:RequestTag/agent_id": "agent-alpha",
      "aws:RequestTag/repository": [
        "agentfarm-greeting-service",
        "agentfarm-feature-flags",
        "agentfarm-word-count"
      ],
      "aws:RequestTag/session_id": "${aws:AgentCoreRuntimeSourceSid}"
    },
    "Null": {
      "aws:AgentCoreRuntimeSourceSid": "false"
    }
  }
}

The alternative is a launcher that requests the token and delivers the token to the agent. A delivered token exists in a file or in a variable before the agent reads it, and each of those places needs protection. If the session requests the token itself, no delivery path exists that needs protection.

The token is valid for 300 seconds, and the agent requests a new token for each request to the gateway. The short lifetime limits the use of a copied token, and no token is valid for the whole session.

The agent process requests the token just before each request, and keeps the token in memory only for that request:

def session_token(session_id: str, repository: str) -> str:
    """A JWT for one request of this session. A boto3 session of this call has the credentials of the session."""
    sts = boto3.Session().client("sts", region_name=REGION, endpoint_url=STS_ENDPOINT)
    return sts.get_web_identity_token(
        Audience=[AUDIENCE], SigningAlgorithm="ES384", DurationSeconds=TOKEN_SECONDS,
        Tags=[{"Key": "agent_id", "Value": AGENT_ID}, {"Key": "session_id", "Value": session_id},
              {"Key": "repository", "Value": repository}])["WebIdentityToken"]


class SessionGateway(Gateway):
    """The MCP client of the agent, with a JWT of the session for every request."""
    def rpc(self, method: str, params: dict | None = None, notification: bool = False):
        self.token = session_token(self._session_id, self._repository)
        try:
            return super().rpc(method, params, notification)
        finally:
            self.token = ""

The function creates a new client for each token, and the reason is a detail of version V2. AgentCore Runtime starts the agent process one time, before the snapshot of the microVM, and every microVM of a session is a copy of that process. A client that the process created at its start keeps the credentials of that moment, without the session ID, and IAM denies its token requests.

The session ID that AgentCore Runtime V2 puts in the credentials is not enough alone, however. IAM guarantees that the token has the ID of the microVM that requests the token, but the component that invokes the runtime chooses that ID, and AgentCore Runtime also accepts the ID of an ended session for a new microVM. For that reason the architecture adds a small function in front of the runtime, the session starter, which is the only component that starts sessions. For each new task, the session starter checks which agent can work on that repository, creates a new ID, records the session in a table and only then starts the runtime. The runtime accepts requests only from the session starter, and the broker accepts only the sessions in that table.

The write in the table succeeds only if no session has that ID, and the runtime receives the same ID:

# The session ID is a value of this function, not of the caller: one session, one ID, one time.
session_id = str(uuid.uuid4())
key = {"session": {"S": f"{agent_id}/{session_id}"}}
_table.put_item(
    TableName=SESSION_TABLE, ConditionExpression="attribute_not_exists(#session)",
    ExpressionAttributeNames={"#session": "session"},
    Item={**key, "state": {"S": "active"},
          # ... the agent, the repository, the issue, the hash of the task, the expiry of the session
          })
response = _runtime.invoke_agent_runtime(
    agentRuntimeArn=AGENT_RUNTIMES[agent_id], runtimeSessionId=session_id,
    payload=json.dumps(work).encode(), contentType="application/json", accept="application/json")

The rule about the session starter is a resource policy of each agent runtime, in the CDK stack. The same policy denies the commands that AgentCore Runtime can run in the microVM of a session, because the credentials of an active session are in that microVM:

everyone = {"AWS": f"arn:aws:iam::{self.account}:root"}
agentcore.CfnResourcePolicy(
    self, f"AgentRuntimePolicy-{agent_id}", resource_arn=runtime.attr_agent_runtime_arn,
    policy=self.to_json_string({"Version": "2012-10-17", "Statement": [
        {"Sid": "OnlyTheSessionStarterInvokes", "Effect": "Deny", "Principal": everyone,
         "Action": ["bedrock-agentcore:InvokeAgentRuntime", "bedrock-agentcore:InvokeAgentRuntimeForUser",
                    "bedrock-agentcore:InvokeAgentRuntimeWithWebSocketStream",
                    "bedrock-agentcore:InvokeAgentRuntimeWithWebSocketStreamForUser"],
         "Resource": runtime.attr_agent_runtime_arn,
         "Condition": {"ArnNotEquals": {"aws:PrincipalArn": names.role_arn(self.account, f"{PREFIX}-starter")}}},
        {"Sid": "NoCommandInTheMicroVMOfASession", "Effect": "Deny", "Principal": everyone,
         "Action": ["bedrock-agentcore:InvokeAgentRuntimeCommand",
                    "bedrock-agentcore:InvokeAgentRuntimeCommandShell"],
         "Resource": runtime.attr_agent_runtime_arn}]}))

Session Credentials from a Broker behind AgentCore Gateway

The simplest design gives the permissions directly to the runtime role and lets the agent send its requests to AWS. With that design, however, CloudTrail does not show the session. There is also a second problem: the runtime role is the same for all sessions, so the role needs permissions on the resources of every session. A session could then change the stack or the parameters of a different session, or perform any AWS operation that the role permits, with arguments that the model chooses.

For this reason the agent must not have any credentials for AWS actions. The agent sends each action as a tool request to an AgentCore Gateway, with the token as a bearer token. The gateway gives the agent a standard interface for tools, and validates the token before any custom code runs.

The tools are the single operations that the agent can request, for example reading a file of the repository, writing a commit, opening the pull request or deploying the test stack. One Lambda function implements all the tools: the broker, which is the target of the gateway. For each tool request, the gateway invokes the broker with the name of the tool and its arguments.

The design puts all tools in one broker because the broker is the place where an identity becomes credentials, and that place should be one. Only the broker role can assume the agent role with a source identity and a role session name. With one function for each tool, every function would need that permission, and every function would become a place where somebody can forge an identity. Every function would also need its own copy of the token check and of the session check. The cost is that one broker works on the repository and also on the stack. For that reason the broker requests credentials for one purpose at a time, as a later section explains.

On each tool request, the broker verifies the token again and checks in the table that the session is active. The second check is deliberate: the credentials come from the broker, so the broker trusts no earlier check.

The broker then requests temporary credentials of an agent role for that session, with the agent ID as source identity and the session ID as role session name. The values come from the claims of the verified token, never from the arguments of the agent.

policy = session_policy(identity["session_id"], identity["repository"], identity["agent_id"], purpose)
# The issue of the session and the hash of its task are session tags: the AssumeRole event in AWS CloudTrail
# then connects the session to the issue and to the text that the agent received.
tags = [{"Key": key, "Value": str(identity[name])}
        for key, name in zip(SESSION_TAGS, ("issue_number", "task_sha256")) if identity.get(name)]
response = _sts.assume_role(
    RoleArn=AGENT_ROLE_ARN,
    RoleSessionName=identity["session_id"],
    SourceIdentity=identity["agent_id"],
    Policy=policy,
    DurationSeconds=900,
    **({"Tags": tags} if tags else {}),
)

The agent ID goes in the source identity because the session cannot change the source identity, and AWS keeps the value when the session assumes a further role. The session ID goes in the role session name because that name is part of the ARN in every event, so a reader sees the session at once.

All sessions share one agent role. A session policy separates the sessions: the broker passes the session policy with each request, and the policy limits the credentials to the stack and the parameter path of that session. The architecture therefore needs no IAM role for each session, and a session cannot change the resources of a different session.

For a deployment, the session policy has only the stack and the parameter path with the ID of the session:

statements = [
    {"Effect": "Allow",
     "Action": ["cloudformation:CreateStack", "cloudformation:UpdateStack", "cloudformation:TagResource",
                "cloudformation:UntagResource", "cloudformation:DescribeStacks",
                "cloudformation:DescribeStackEvents"],
     "Resource": f"arn:aws:cloudformation:{REGION}:{ACCOUNT}:stack/{STACK_PREFIX}{session_id}/*"},
    {"Effect": "Allow",
     "Action": ["ssm:PutParameter", "ssm:AddTagsToResource", "ssm:RemoveTagsFromResource",
                "ssm:ListTagsForResource", "ssm:GetParameters", "ssm:DeleteParameter"],
     "Resource": f"arn:aws:ssm:{REGION}:{ACCOUNT}:parameter/agentfarm/{session_id}/*"}]

As a result, every CloudTrail event of a session names the agent and the session. This is the CreateStack event of a session of agent-alpha, with some fields omitted:

{
  "eventSource": "cloudformation.amazonaws.com",
  "eventName": "CreateStack",
  "userIdentity": {
    "type": "AssumedRole",
    "arn": "arn:aws:sts::111122223333:assumed-role/agentfarm-runtime-agent/c2f29e87-64b9-412b-a304-43f3c74aeec2",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "arn": "arn:aws:iam::111122223333:role/agentfarm-runtime-agent"
      },
      "sourceIdentity": "agent-alpha"
    }
  },
  "requestParameters": {
    "stackName": "agentfarm-c2f29e87-64b9-412b-a304-43f3c74aeec2"
  }
}

The broker performs the action and returns the result. The credentials never reach the agent, so a hostile task has nothing to send outside.

A tool request, from the token to the action
A tool request, from the token to the action

Agent Attribution in GitHub Commits and Pull Requests

CloudTrail covers AWS, but the code is on GitHub, and GitHub has no source identity. On GitHub the identity of an agent is a GitHub App, one for each agent. A GitHub App has two advantages over a personal access token: the App is an identity of its own, and an App token is valid for one hour and for one repository.

The broker writes the commit and opens the pull request. For that, the broker must act as the App of the agent on GitHub, and that requires a signature with the private key of the App. The key is in AWS KMS: the broker asks KMS for the signature with the credentials of the session, and nobody reads the key, the broker included. The agent never sees the key or the token.

The usual choice is a secret for the key. With a secret, however, the broker would have the key in memory for every request, and a single flaw in the broker would be enough to leak the key. With KMS, a flaw in the broker can at most request signatures while the broker runs. Each agent also has its own key, with an alias that contains the agent ID. The policy of the agent role permits a signature only with the alias that contains the source identity of the session, which is the agent ID that the broker wrote in the credentials. The credentials of a session can therefore sign only as the App of their own agent, never as the App of a different agent.

In the CDK stack, the policy variable ${aws:SourceIdentity} builds the alias from the credentials, so one statement covers all agents:

agent_role.add_to_policy(iam.PolicyStatement(
    actions=["kms:Sign"], resources=[f"arn:aws:kms:{self.region}:{self.account}:key/*"],
    conditions={
        "StringEquals": {
            "kms:RequestAlias": names.GITHUB_APP_KEY_ALIAS_PREFIX + "${aws:SourceIdentity}",
            "kms:SigningAlgorithm": names.GITHUB_APP_SIGNING_ALGORITHM, "kms:MessageType": "RAW"},
        "Null": {"aws:CalledViaFirst": "true"}}))

On GitHub the commit has the App of the agent as author, with the verified mark. At the end of the commit message and of the pull request, the broker adds two lines with the agent ID and the session ID. Those lines are trustworthy because the broker writes the lines from the verified token, after it removes any lines of the same kind from the text of the agent. This is the commit of the session of the CreateStack event above:

Fix count_words for repeated spaces, empty text and other white space; version 1.0.1

Split on any white space with str.split() instead of a single space.

Agent-Id: agent-alpha
Session-Id: c2f29e87-64b9-412b-a304-43f3c74aeec2

The signature is also the link between GitHub and CloudTrail: for every repository action of the broker, CloudTrail has a KMS Sign event with the agent and the session. This is the Sign event of the same session, with some fields omitted:

{
  "eventSource": "kms.amazonaws.com",
  "eventName": "Sign",
  "userIdentity": {
    "type": "AssumedRole",
    "arn": "arn:aws:sts::111122223333:assumed-role/agentfarm-runtime-agent/c2f29e87-64b9-412b-a304-43f3c74aeec2",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "arn": "arn:aws:iam::111122223333:role/agentfarm-runtime-agent"
      },
      "sourceIdentity": "agent-alpha"
    }
  },
  "requestParameters": {
    "signingAlgorithm": "RSASSA_PKCS1_V1_5_SHA_256",
    "messageType": "RAW",
    "keyId": "alias/agentfarm/github-app/agent-alpha"
  }
}

On GitHub, the pull request and the commit of that session have the App of the agent as author and the same session ID:

The pull request and its commit, with the App as author, the verified mark and the identity lines
The pull request and its commit, with the App as author, the verified mark and the identity lines

The audit chain is now complete. The live stack has the merge commit as a tag. From the merge, an audit can follow the pull request and the commits of the broker, which name the App of the agent, the agent ID and the session ID. The session table records, for each session, the source issue and the person who opened that issue.

Isolation of Parallel Agent Sessions with IAM Session Policies

A factory runs the same agent many times in parallel, so the isolation that matters is the isolation between sessions of the same agent. Each session has its own microVM, token, branch, pull request and stack, and the credentials of the broker are valid for one session only.

If the template of a session names a parameter in the path of a different session, CloudFormation sends the request with the credentials of the session that started the deployment, and IAM denies the request. CloudTrail keeps the denied event with the ID of that session, so even the attempt has an author.

The token has the same protection. A session that requests a token with the ID of a different session receives a denial from IAM, also when both sessions belong to the same agent.

The separation between different agents works the same way. Each agent has its own GitHub App and its own KMS key, and a session can sign only with the key of its own agent.

Isolated Workspace for Untrusted Code with AgentCore Code Interpreter

Attribution is useful after an action. Before the action, the architecture must limit the resources that a session can use, because the input of a session can be hostile.

The text of a task and the content of a repository are input that nobody reviewed, and the model writes its commands from that input. The code must run somewhere, and the place where the code runs decides the damage that hostile input can do.

The agent process, the program that talks to the model and to the gateway, runs in the microVM that AgentCore Runtime gives to the session. AgentCore Runtime gives that microVM the credentials of the runtime execution role, the same credentials that the agent needs for the token request and for the model. Code from the model in that microVM would run in the same place as those credentials.

The code runs in a separate workspace instead. AgentCore Code Interpreter is a managed AgentCore service that runs code in isolated sessions, each one separate from the other sessions. A Code Interpreter has a network mode and, as an option, an execution role, which is the IAM role that gives the code access to AWS.

The workspace of this architecture has no execution role and a network mode without internet access. The code in the workspace therefore has no AWS credentials to use or to steal, and cannot reach an external address, so the code cannot exfiltrate data. The model also has no tool that runs commands in the microVM of the agent: the code of the model always runs in the workspace.

In the CDK stack, the important part of the workspace is a property that is absent: the resource has no execution_role_arn.

WORKSPACE_NETWORK = "SANDBOX"   # the network mode of the workspace: no path to the internet

sandbox = agentcore.CfnCodeInterpreterCustom(
    self, "Workspace", name="agentfarm_workspace_isolated",
    description="Workspace of a session: builds and tests. No credentials. No path to the internet",
    network_configuration=agentcore.CfnCodeInterpreterCustom.CodeInterpreterNetworkConfigurationProperty(
        network_mode=WORKSPACE_NETWORK))

The agent copies the files of the repository to the workspace, and at the end reads the changed files for the commit. Without a network, however, the workspace cannot download the dependencies of a build, for example with pip install. For that reason the agent process, which can reach the Python package index from the microVM, downloads the packages that the build needs, by name and version, and copies the packages to the workspace as files. In the workspace, the code installs the packages from those files.

Agent Process Sandbox with Strands Box

The agent itself, the program that talks to the model and to the gateway, runs in the microVM of the runtime, separate from the workspace. That microVM has the credentials of the role and a path to the internet. No text of the model runs in the microVM, but a flaw in the agent process or in one of its libraries would give an attacker the credentials and also the network path.

The strongest solution is a VPC for the runtime, with private subnets and VPC endpoints, which removes the path to the internet. The cost is an hourly price and a private package repository to maintain.

Strands Box is an alternative without a VPC: a sandbox for agent processes from the Strands Agents project, in developer preview today. A policy decides the files and the network destinations that the process can use. All outbound requests go to a gateway of Box, which signs the AWS requests itself, so the process has only placeholder credentials. Box is a weaker boundary than a VPC, because Box is software in the same microVM as the agent.

With Box, the agent process reaches only the hosts that it needs: AWS STS, Amazon Bedrock, AgentCore, the gateway of the system and the Python package index. The binding between session and token still works, because the gateway of Box signs with the credentials of the microVM.

Box cannot stop one request, however. AWS STS is one of the permitted hosts, and AssumeRoleWithWebIdentity needs no AWS signature, because the token decides the request. A process with the token of its session could therefore send the token to STS for a role in a different AWS account. If that account trusted the tokens of the first account, STS would accept the request, and the role session name would give the process 64 characters of free text.

A VPC endpoint for STS, with a policy for the roles of the account, closes that path. The same limit applies to every allow list of host names: a host name cannot limit a request to one AWS account, because an AWS endpoint serves every AWS account.

Single-Purpose Credentials for CloudFormation Deployments

CloudFormation resolves a dynamic reference in a template with the credentials of the principal that starts the stack operation. A template can therefore make CloudFormation read a parameter or a secret, if those credentials can read the value.

In this architecture the template is input of the agent. If the deployment used the same credentials that the broker uses for GitHub, a hostile task could lead the agent to a template that copies the data of the GitHub App into a resource of the stack.

For that reason the broker requests credentials for one purpose at a time. The credentials for the stack can change only the stack and the parameter path of the session. The credentials for the repository can read the IDs of the App and request the signature, and only with a direct request: IAM denies a request that an AWS service sends for the role. That is the condition "Null": {"aws:CalledViaFirst": "true"} in the statement of the previous section: the key is absent when the principal sends the request directly, and present when a service such as CloudFormation sends the request with the credentials of the principal. A template that tries to read a value of the App gets no stack, and CloudTrail records the denied read.

The private key is protected in every case, because the key is in KMS and no credentials can read the key.

Managed Services and Custom Code in an AgentCore Architecture

Before version V2 of AgentCore Runtime, the credentials of a session did not contain the session ID, and IAM had no way to distinguish one session from another. For the same result, the component that starts the session would have to request the token and deliver the token to the agent. That delivery would need protection as well: the token would be in a file or in a variable, and a private network and a closed storage would be necessary, so that nobody else could read the token. With AgentCore Runtime V2 the session starter still starts the session, but the session starter never touches the token: the session requests the token itself, and IAM checks that the ID belongs to that session. The private network and the storage for the delivery are no longer necessary.

The architecture on AWS
The architecture on AWS

The custom code of the architecture is a small set of Lambda functions: the session starter in front of the runtime and two functions at the gateway, the interceptor and the broker. A natural next question is whether a managed feature can replace the custom code. Today the answer is no, and the three possible alternatives show the reason.

Managed rules at the gateway. Policy in AgentCore applies rules written in Cedar to every request, and with Dogwood also rules about the sequence of requests. Policy in AgentCore can do the checks on the token and on the session in place of the interceptor, and can end a session. A rule cannot compare a tag of the token with an argument of the request, however, so the design would need one rule for each session, with a creation and a deletion for every session. The temporal rules of Dogwood decide from the earlier requests of the same session: for example, a rule can permit a deployment only after a commit. To group the requests of one session, however, the gateway uses a header that the sender of the request chooses, and the sender is the agent. A new value for that header starts with an empty history, and the value of a different session reuses the history of that session: today the rule does not follow the real session of the agent. The code would move more than it would disappear, and the broker would still be necessary for every action that needs credentials.

The gateway that sends AWS requests itself. Smithy is the language that AWS uses to describe the APIs of its services. AgentCore Gateway accepts the Smithy model of an AWS service as a target, and creates the tools from that model, one tool for each operation: with a Smithy target, the gateway sends the request to AWS, with no function in between. For that request, however, the gateway uses the gateway role, which is the same for all sessions, and CloudTrail shows that role, without agent and without session.

IAM alone, without a gateway. The session can request the credentials of the agent role itself, with a trust policy that requires the agent and the session as names. CloudTrail stays complete and IAM separates the sessions, but the agent then has the credentials for its AWS actions.

These gaps are limits of the services of today, more than limits of the idea. For each job of the custom code, the table explains the reason for the code today and the feature that AWS would need to offer to make the code unnecessary.

Job of the custom code todayReason that AWS is not enough todayFeature that AWS would need to offer
The interceptor checks that the role of an allowed agent requested the token, and passes the token to the broker. The broker verifies the token again and checks in the table that the session is active.The gateway can compare a claim of the token only with one fixed value, but each agent has its own role. The gateway passes no verified identity to the function behind the gateway, and the gateway does not know if the runtime session is still active.Gateway rules that read the tags of the token and know the state of the runtime session. The verified identity in the request to the function.
The broker requests temporary credentials with the agent as source identity and the session as role session name.When the gateway sends a request to AWS, the gateway uses its own role, which is the same for all sessions.A gateway that requests the credentials for every request, with agent and session from the token.
The broker acts as the App of the agent on GitHub and writes commits and pull requests with the identity lines. The broker deploys only the committed template.AgentCore Identity supports GitHub OAuth Apps, which act for a user, but not GitHub Apps. The gateway cannot set an argument from the token, and CloudFormation cannot create a stack directly from a commit.GitHub Apps as a credential provider. Arguments that the gateway sets from the token, and a stack from a commit.

From Human Review to Autonomous Deployments

While a person merges, the review is the last check before the live environment. To remove the review, operators need to know the guarantees that remain when nobody reviews the change, and the previous sections describe those guarantees.

The agent cannot promote its own change, so the step to the live environment is the only path. Each session is limited to its own resources, and until that step happens an unwanted change stays confined to the test stack of the session. The test stack shows that the change deploys before the change reaches production. Every action and every commit names the agent and the session, so an automated check, and later an audit, work on reliable data.

Autonomy needs automated checks that can replace the review, such as tests on the pull request and checks on the template. Those checks must be deterministic: tests and checks written as normal code, which give the same result for the same input every time. A check by a different agent would read the same pull request, and the same hostile text could influence that agent too.

Known Limits of Agent Attribution on AWS

  • Bearer token. A process with the token of an active session can use the token from any network, for at most 300 seconds.
  • Preview software. Strands Box is a developer preview and runs in the same microVM as the agent. A VPC for the runtime is still the stronger boundary.
  • Deployment control. A principal that can change the code of the functions or the trust policy of the agent role can forge an identity. The deployment pipeline is part of the trust boundary.

Comments

Loading comments…