Skip to main content

Sandbox isolation

Code that an agent writes runs in AWS Lambda inside your account, never in the Studio services themselves. This page describes the boundaries around one execution: the function it runs in, the working directory and home it gets, how the handler keeps its own credentials away from the code, the per-invocation AWS credentials that are narrowed to one user's files and one agent's secret, and what the code can and cannot reach.

Who this is for​

Security engineers assessing what untrusted, model-written code can do inside your account.

Before you start​

Two terms. An execution environment is a container image your users choose for an agent (a built-in image or a custom one). An execution is one run of a script or shell command that the coder submits. Environments are managed in Studio; see Execution environments.

One function per environment, one invocation per execution​

agent-management creates one Lambda function per execution environment, alphaagent-env-<environment id>, from the environment's image in your alphaagent-envs repository, placed in the deployment's private subnets on the sandbox security group. Each execution is one invocation of that function: the code-interpreter service sends the code, the workspace prefix to mirror and the agent's identity, and the handler inside the function does the rest.

A warm Lambda container may serve several invocations in a row, for different agents and different users of the same environment. Everything below exists so that one invocation cannot leave anything for the next one to find.

Fresh working directory​

Every execution runs in a new directory under /tmp, created with mkdtemp and mode 0700, named /tmp/.aa-work-<random>. The handler mirrors the requested project folders down from the workspaces bucket into it, runs the code with that directory as the current directory, uploads only the files the run created or changed, and removes the directory. There is no predictable path that survives between executions, so code cannot plant a file where a later user's code would import it.

Downloads are served from a per-project cache whose index lives only in the handler's memory; every cache hit is re-verified by SHA-256 before it is hard-linked into the working directory, and anything in the cache the index does not vouch for is removed.

Private home per scope​

The child process gets its own HOME, /tmp/home-<hash of scope>, where the scope is the invoking agent, else the owning user, else the execution itself. The same agent's later executions on the same warm container find what it installed with pip install --user; a different agent never does. This closes the path where one user's code writes a startup hook (usercustomize.py, a .pth file, a pip.conf or a shell rc file) that the next user's code would execute.

Re-execution with a clean environment​

Lambda hands a function its IAM role credentials as environment variables, and a child process can normally read its parent's starting environment from /proc/<ppid>/environ. The handler prevents this in two ways at import time:

  1. It reads its credentials, removes them from the environment, passes them to itself over a pipe, and re-executes itself (execve) with an environment that carries none. The credentials exist only in the serving process's memory.
  2. It re-executes from an exec-only copy of the interpreter, which makes the serving process non-dumpable from its first instruction. A same-user child that tries to open /proc/<ppid>/environ, /proc/<ppid>/mem or /proc/<ppid>/maps gets PermissionError.

The handler records the outcome of both measures in its capability probe, so the service can see when a container did not achieve them.

Per-invocation credentials, narrowed by a session policy​

Two IAM roles are involved, both created by the core stack:

  • The Lambda execution role holds no data permissions. It can start the function (pull the image, create network interfaces, write its own logs, read the staged handler from one prefix of the overflow bucket) and it can assume one role: the sandbox scoped role.
  • The sandbox scoped role is trusted only by the execution role. Its policy allows reads and writes on the workspaces bucket and secretsmanager:GetSecretValue on secrets named alphaagent-connector-creds-*. Nothing else.

The handler never uses the scoped role's full policy. Every AssumeRole it makes carries a session policy that narrows the session to this invocation: read on users/<user id>/ (plus a deployment-wide read prefix in a governed deployment), write on users/<user id>/ only, ListBucket conditioned on the same prefixes, and read of the one secret alphaagent-connector-creds-<agent id> for the invoking agent. The effective permission is the intersection of the role's policy and the session policy, so no session can reach another user's files or another agent's secret. Sessions last at most 3600 seconds and are minted per invocation.

The child process that runs your agent's code receives none of these credentials. Its environment is the image's own plus this invocation's connector variables; see Secrets and connector credentials.

What an agent's code can reach​

ReachableHow
The files of its own conversation or runMirrored into the working directory by the handler; changes are uploaded back to users/<user id>/...
The connectors assigned to its agentCONN_<connector id>_* variables in the child's environment, resolved at invocation
Your systems and the public internetOutbound TCP 443 only, through the NAT gateway; the sandbox security group has no other egress rule
Packages it installsInto its private HOME for the life of the warm container

What an agent's code cannot reach​

Not reachableWhy
The handler's AWS credentialsRemoved from the environment before the child exists; the serving process is non-dumpable
Other users' workspace filesNot mirrored; the session policy denies the prefix
Other agents' connector secrets, service configuration secrets, the licence secretThe scoped role reads only alphaagent-connector-creds-*, and the session policy narrows that to one secret
The ECS task roles, Bedrock, DynamoDB tablesThe child has no AWS credentials at all
A previous execution's working directoryRemoved after each execution
Ports other than 443Security group egress

One bound the design states plainly: every child of a warm container is the same operating-system user on the same /tmp, so a background process that one execution leaves behind is not stopped by these measures. The Lambda timeout ends the invocation.

Steps: verify the boundaries in your account​

  1. In IAM, open alphaagent-<stack environment>-lambda-execution-role, where the middle segment is the core stack's Environment parameter (prod by default), not an execution environment. Its inline policy should contain sts:AssumeRole on the sandbox scoped role, s3:GetObject on one handler prefix, logs, network interface and ECR read actions, and nothing on your data.
  2. Open alphaagent-<stack environment>-sandbox-scoped-role. Its trust policy names only the execution role; its policy covers the workspaces bucket and alphaagent-connector-creds-*.
  3. In Lambda, open any alphaagent-env-* function. Under Configuration, the environment variables hold AA_HANDLER_URI (the storage prefix the current handler is downloaded from) and no credential and no CONN_ variable; Image configuration shows the entry point Studio set (a Python bootstrap), the command handler.handler and the working directory /opt; VPC shows the private subnets and the sandbox security group.
  4. In EC2, open that security group. Outbound: TCP 443 to 0.0.0.0/0 only. Inbound: none.
  5. If CloudTrail is enabled, filter AssumeRole events for the scoped role. Each carries a session policy and a session name derived from the invocation.

What you should see​

  • No alphaagent-env-* function with credentials or connector values in its configuration.
  • Every sandbox AssumeRole event shows DurationSeconds of at most 3600 and a policy document.
  • Sandbox log groups (/aws/lambda/alphaagent-env-*) carry handler messages, including "credential isolation: re-executing with a clean environment".

Limits​

  • An invocation runs for at most 900 seconds (the Lambda maximum); memory up to 10,240 MB and ephemeral storage between 2,048 MB and 10,240 MB, set per environment.
  • Egress is TCP 443 only. A data source on another port is not reachable from a sandbox.
  • The measures above are process boundaries inside one Lambda container, not separate containers per user.

If something goes wrong​

  • An execution fails with an access error on PutObject: the handler tags every object it writes under a run prefix, and the scoped role must allow PutObjectTagging; a service control policy that removes it breaks uploads.
  • An execution cannot reach a data source: check that the source accepts TCP 443 from the NAT gateway's Elastic IP.
  • The capability probe reports non_dumpable: false: the container could not re-execute from the exec-only copy; the handler logs why. Raise it with us with the log line.